@weblock-wallet/sdk 0.1.66 → 0.1.68

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
@@ -1778,11 +1778,11 @@ var storage = import_localforage.default.createInstance({
1778
1778
  description: "WeBlock Wallet SDK Secure Storage"
1779
1779
  });
1780
1780
  var LocalForage = {
1781
- async save(key, value, expiry) {
1781
+ async save(key, value, expiryEpochMs) {
1782
1782
  try {
1783
1783
  const item = {
1784
1784
  value,
1785
- expiry
1785
+ expiryEpochMs
1786
1786
  };
1787
1787
  await storage.setItem(key, item);
1788
1788
  } catch (err) {
@@ -1792,10 +1792,8 @@ var LocalForage = {
1792
1792
  async get(key) {
1793
1793
  try {
1794
1794
  const item = await storage.getItem(key);
1795
- if (!item) {
1796
- return null;
1797
- }
1798
- if (item.expiry && Date.now() > item.expiry * 1e3) {
1795
+ if (!item) return null;
1796
+ if (item.expiryEpochMs && Date.now() > item.expiryEpochMs) {
1799
1797
  await storage.removeItem(key);
1800
1798
  return null;
1801
1799
  }
@@ -1938,26 +1936,6 @@ var AuthService = class {
1938
1936
  var import_ethers2 = require("ethers");
1939
1937
  var import_bip39 = require("bip39");
1940
1938
 
1941
- // src/utils/secrets.ts
1942
- var import_shamir_secret_sharing = require("shamir-secret-sharing");
1943
- var Secrets = {
1944
- async split(secret, total, threshold) {
1945
- if (secret.startsWith("0x")) {
1946
- secret = secret.substring(2);
1947
- }
1948
- const array = await (0, import_shamir_secret_sharing.split)(
1949
- new Uint8Array(Buffer2.from(secret, "hex")),
1950
- total,
1951
- threshold
1952
- );
1953
- return array.map((item) => Buffer2.from(item).toString("hex"));
1954
- },
1955
- async combine(shares) {
1956
- const array = shares.map((item) => new Uint8Array(Buffer2.from(item, "hex")));
1957
- return `0x${Buffer2.from(await (0, import_shamir_secret_sharing.combine)(array)).toString("hex")}`;
1958
- }
1959
- };
1960
-
1961
1939
  // node_modules/.pnpm/@jspm+core@2.1.0/node_modules/@jspm/core/nodelibs/browser/chunk-CcCWfKp1.js
1962
1940
  var exports$22 = {};
1963
1941
  var _dewExec$12 = false;
@@ -103973,6 +103951,26 @@ var webcrypto = exports11.webcrypto;
103973
103951
  var getRandomValues = exports11.getRandomValues;
103974
103952
  var randomUUID = exports11.randomUUID;
103975
103953
 
103954
+ // src/utils/secrets.ts
103955
+ var import_shamir_secret_sharing = require("shamir-secret-sharing");
103956
+ var Secrets = {
103957
+ async split(secret, total, threshold) {
103958
+ if (secret.startsWith("0x")) {
103959
+ secret = secret.substring(2);
103960
+ }
103961
+ const array = await (0, import_shamir_secret_sharing.split)(
103962
+ new Uint8Array(Buffer2.from(secret, "hex")),
103963
+ total,
103964
+ threshold
103965
+ );
103966
+ return array.map((item) => Buffer2.from(item).toString("hex"));
103967
+ },
103968
+ async combine(shares) {
103969
+ const array = shares.map((item) => new Uint8Array(Buffer2.from(item, "hex")));
103970
+ return `0x${Buffer2.from(await (0, import_shamir_secret_sharing.combine)(array)).toString("hex")}`;
103971
+ }
103972
+ };
103973
+
103976
103974
  // src/utils/crypto.ts
103977
103975
  var ed = __toESM(require("@noble/ed25519"), 1);
103978
103976
  function urlEncode(pemKey) {
@@ -104020,6 +104018,65 @@ var Crypto = {
104020
104018
  }
104021
104019
  };
104022
104020
 
104021
+ // src/utils/numbers.ts
104022
+ var import_ethers = require("ethers");
104023
+ var TokenAmount = class {
104024
+ /**
104025
+ * Wei 단위의 값을 사용자가 읽을 수 있는 형태로 변환
104026
+ * @param value - Wei 단위의 값 (hex string 또는 decimal string)
104027
+ * @param decimals - 토큰의 소수점 자리수
104028
+ * @returns 변환된 문자열
104029
+ */
104030
+ static fromWei(value, decimals) {
104031
+ try {
104032
+ return (0, import_ethers.formatUnits)(value, decimals);
104033
+ } catch (error) {
104034
+ console.error("Error converting from wei:", error);
104035
+ return "0.0";
104036
+ }
104037
+ }
104038
+ /**
104039
+ * 사용자 입력값을 Wei 단위로 변환
104040
+ * @param value - 사용자 입력값 (예: "1.5")
104041
+ * @param decimals - 토큰의 소수점 자리수
104042
+ * @returns Wei 단위의 값
104043
+ */
104044
+ static toWei(value, decimals) {
104045
+ try {
104046
+ return (0, import_ethers.parseUnits)(value, decimals).toString();
104047
+ } catch (error) {
104048
+ console.error("Error converting to wei:", error);
104049
+ return "0";
104050
+ }
104051
+ }
104052
+ /**
104053
+ * 표시용 포맷으로 변환
104054
+ * @param value - Wei 단위의 값
104055
+ * @param decimals - 토큰의 소수점 자리수
104056
+ * @param maxDecimals - 최대 표시할 소수점 자리수 (기본값: 4)
104057
+ * @returns 포맷된 문자열
104058
+ */
104059
+ static format(value, decimals, maxDecimals = 4) {
104060
+ const fullNumber = this.fromWei(value, decimals);
104061
+ const [beforeDecimal, afterDecimal = ""] = fullNumber.split(".");
104062
+ return `${beforeDecimal}.${afterDecimal.slice(0, maxDecimals)}`;
104063
+ }
104064
+ /**
104065
+ * 값이 유효한지 검증
104066
+ * @param value - 검증할 값
104067
+ * @returns 유효성 여부
104068
+ */
104069
+ static isValid(value) {
104070
+ return /^\d*\.?\d*$/.test(value) && value !== "";
104071
+ }
104072
+ };
104073
+ var DECIMALS = {
104074
+ ETH: 18,
104075
+ USDC: 6,
104076
+ USDT: 6,
104077
+ DAI: 18
104078
+ };
104079
+
104023
104080
  // src/types/error.ts
104024
104081
  var SDKErrorCode = /* @__PURE__ */ ((SDKErrorCode2) => {
104025
104082
  SDKErrorCode2["NOT_INITIALIZED"] = "NOT_INITIALIZED";
@@ -104088,65 +104145,6 @@ var SignInStatus = /* @__PURE__ */ ((SignInStatus2) => {
104088
104145
  return SignInStatus2;
104089
104146
  })(SignInStatus || {});
104090
104147
 
104091
- // src/utils/numbers.ts
104092
- var import_ethers = require("ethers");
104093
- var TokenAmount = class {
104094
- /**
104095
- * Wei 단위의 값을 사용자가 읽을 수 있는 형태로 변환
104096
- * @param value - Wei 단위의 값 (hex string 또는 decimal string)
104097
- * @param decimals - 토큰의 소수점 자리수
104098
- * @returns 변환된 문자열
104099
- */
104100
- static fromWei(value, decimals) {
104101
- try {
104102
- return (0, import_ethers.formatUnits)(value, decimals);
104103
- } catch (error) {
104104
- console.error("Error converting from wei:", error);
104105
- return "0.0";
104106
- }
104107
- }
104108
- /**
104109
- * 사용자 입력값을 Wei 단위로 변환
104110
- * @param value - 사용자 입력값 (예: "1.5")
104111
- * @param decimals - 토큰의 소수점 자리수
104112
- * @returns Wei 단위의 값
104113
- */
104114
- static toWei(value, decimals) {
104115
- try {
104116
- return (0, import_ethers.parseUnits)(value, decimals).toString();
104117
- } catch (error) {
104118
- console.error("Error converting to wei:", error);
104119
- return "0";
104120
- }
104121
- }
104122
- /**
104123
- * 표시용 포맷으로 변환
104124
- * @param value - Wei 단위의 값
104125
- * @param decimals - 토큰의 소수점 자리수
104126
- * @param maxDecimals - 최대 표시할 소수점 자리수 (기본값: 4)
104127
- * @returns 포맷된 문자열
104128
- */
104129
- static format(value, decimals, maxDecimals = 4) {
104130
- const fullNumber = this.fromWei(value, decimals);
104131
- const [beforeDecimal, afterDecimal = ""] = fullNumber.split(".");
104132
- return `${beforeDecimal}.${afterDecimal.slice(0, maxDecimals)}`;
104133
- }
104134
- /**
104135
- * 값이 유효한지 검증
104136
- * @param value - 검증할 값
104137
- * @returns 유효성 여부
104138
- */
104139
- static isValid(value) {
104140
- return /^\d*\.?\d*$/.test(value) && value !== "";
104141
- }
104142
- };
104143
- var DECIMALS = {
104144
- ETH: 18,
104145
- USDC: 6,
104146
- USDT: 6,
104147
- DAI: 18
104148
- };
104149
-
104150
104148
  // src/core/services/wallet.ts
104151
104149
  var ERC20_ABI = [
104152
104150
  "function balanceOf(address owner) view returns (uint256)",
@@ -104172,7 +104170,7 @@ var WalletService = class {
104172
104170
  this.walletAddress = null;
104173
104171
  }
104174
104172
  /**
104175
- * Wallet PIN/password mismatch handling.
104173
+ * Password/PIN mismatch detection for decrypt errors.
104176
104174
  */
104177
104175
  isInvalidPasswordError(error) {
104178
104176
  if (!error) return false;
@@ -104191,9 +104189,8 @@ var WalletService = class {
104191
104189
  return /^[0-9]{6}$/.test(pin);
104192
104190
  }
104193
104191
  /**
104194
- * deviceSecret "PIN과 무관한 로컬 복구용 비밀값"입니다.
104195
- * - 같은 디바이스에서만 PIN reset이 가능하도록 하는 역할
104196
- * - 서버에는 절대 전달하지 않습니다.
104192
+ * deviceSecret: local-only secret for device recovery material.
104193
+ * This must never be sent to server.
104197
104194
  */
104198
104195
  async getOrCreateDeviceSecret() {
104199
104196
  const key = STORAGE_KEYS.deviceSecret(this.orgHost);
@@ -104203,19 +104200,20 @@ var WalletService = class {
104203
104200
  await LocalForage.save(key, secret);
104204
104201
  return secret;
104205
104202
  }
104203
+ /**
104204
+ * PATCH APPLIED:
104205
+ * Always overwrite encryptedShare2_device when we have a fresh share2.
104206
+ * Keeping an old device share causes (share1 new + share2 old) => different derived wallet.
104207
+ */
104206
104208
  async ensureDeviceEncryptedShare2(share2Plain, firebaseId) {
104207
104209
  const encryptedKey = STORAGE_KEYS.encryptedShare2Device(this.orgHost);
104208
- const existing = await LocalForage.get(encryptedKey);
104209
- if (existing) return;
104210
104210
  const deviceSecret = await this.getOrCreateDeviceSecret();
104211
104211
  const encrypted = Crypto.encryptShare(share2Plain, deviceSecret, firebaseId);
104212
104212
  await LocalForage.save(encryptedKey, encrypted);
104213
104213
  }
104214
104214
  async getAddress() {
104215
104215
  try {
104216
- if (this.walletAddress) {
104217
- return this.walletAddress;
104218
- }
104216
+ if (this.walletAddress) return this.walletAddress;
104219
104217
  const savedAddress = await LocalForage.get(
104220
104218
  STORAGE_KEYS.walletAddress(this.orgHost)
104221
104219
  );
@@ -104246,7 +104244,6 @@ var WalletService = class {
104246
104244
  const firebaseId = await LocalForage.get(
104247
104245
  STORAGE_KEYS.firebaseId(this.orgHost)
104248
104246
  );
104249
- console.log("1. FirebaseId:", firebaseId);
104250
104247
  if (!firebaseId) {
104251
104248
  throw new SDKError("Not logged in", "AUTH_REQUIRED" /* AUTH_REQUIRED */);
104252
104249
  }
@@ -104268,27 +104265,18 @@ var WalletService = class {
104268
104265
  "WALLET_ALREADY_EXISTS" /* WALLET_ALREADY_EXISTS */
104269
104266
  );
104270
104267
  }
104271
- console.log("2. Generating wallet...");
104272
104268
  const mnemonic = (0, import_bip39.generateMnemonic)();
104273
104269
  const seed = await (0, import_bip39.mnemonicToSeed)(mnemonic);
104274
104270
  const wallet = new import_ethers2.Wallet(seed.slice(0, 32).toString("hex"));
104275
- console.log("3. Wallet created:", {
104276
- address: wallet.address,
104277
- publicKey: wallet.signingKey.publicKey
104278
- });
104279
- console.log("4. Splitting private key...");
104280
104271
  const shares = await Secrets.split(wallet.privateKey, 3, 2);
104281
104272
  const [share1, share2, share3] = shares;
104282
- console.log("6. Encrypting shares...");
104283
104273
  const encryptedShare2 = Crypto.encryptShare(share2, password, firebaseId);
104284
104274
  const encryptedShare3 = Crypto.encryptShare(share3, password, firebaseId);
104285
- console.log("8. Saving encryptedShare2 to LocalForage...");
104286
104275
  await LocalForage.save(
104287
104276
  STORAGE_KEYS.encryptedShare2(this.orgHost),
104288
104277
  encryptedShare2
104289
104278
  );
104290
104279
  await this.ensureDeviceEncryptedShare2(share2, firebaseId);
104291
- console.log("10. Creating wallet on server...");
104292
104280
  await this.walletClient.createWallet({
104293
104281
  address: wallet.address,
104294
104282
  publicKey: wallet.signingKey.publicKey,
@@ -104302,7 +104290,6 @@ var WalletService = class {
104302
104290
  );
104303
104291
  return wallet.address;
104304
104292
  } catch (error) {
104305
- console.error("Error in create wallet:", error);
104306
104293
  if (error instanceof SDKError) throw error;
104307
104294
  throw new SDKError(
104308
104295
  "Failed to create wallet",
@@ -104357,8 +104344,13 @@ var WalletService = class {
104357
104344
  share2 = decryptShareOrThrow(encryptedShare2);
104358
104345
  await LocalForage.save(STORAGE_KEYS.share2(this.orgHost), share2);
104359
104346
  } else {
104360
- const share3 = decryptShareOrThrow(walletInfo.encryptedShare3);
104361
- const privateKey2 = await Secrets.combine([walletInfo.share1, share3]);
104347
+ const share3 = decryptShareOrThrow(
104348
+ walletInfo.encryptedShare3
104349
+ );
104350
+ const privateKey2 = await Secrets.combine([
104351
+ walletInfo.share1,
104352
+ share3
104353
+ ]);
104362
104354
  const wallet2 = new import_ethers2.Wallet(privateKey2);
104363
104355
  const newShares = await Secrets.split(wallet2.privateKey, 3, 2);
104364
104356
  const [newShare1, newShare2, newShare3] = newShares;
@@ -104385,7 +104377,10 @@ var WalletService = class {
104385
104377
  return wallet2.address;
104386
104378
  }
104387
104379
  }
104388
- const privateKey = await Secrets.combine([walletInfo.share1, share2]);
104380
+ const privateKey = await Secrets.combine([
104381
+ walletInfo.share1,
104382
+ share2
104383
+ ]);
104389
104384
  const wallet = new import_ethers2.Wallet(privateKey);
104390
104385
  await this.ensureDeviceEncryptedShare2(share2, firebaseId);
104391
104386
  this.walletAddress = wallet.address;
@@ -104398,7 +104393,6 @@ var WalletService = class {
104398
104393
  } catch (error) {
104399
104394
  this.walletAddress = null;
104400
104395
  await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
104401
- console.error("Error in retrieveWallet:", error);
104402
104396
  if (error instanceof SDKError) throw error;
104403
104397
  throw new SDKError(
104404
104398
  "Failed to retrieve wallet",
@@ -104408,9 +104402,7 @@ var WalletService = class {
104408
104402
  }
104409
104403
  }
104410
104404
  /**
104411
- * NEW: PIN reset (프라이빗키/주소 유지)
104412
- * - 같은 디바이스에 남아있는 encryptedShare2_device를 이용
104413
- * - 서버는 PATCH /v1/wallets/keys 로 share1 / encryptedShare3 만 업데이트
104405
+ * PIN reset (same private key/address) using encryptedShare2_device.
104414
104406
  */
104415
104407
  async resetPin(newPassword) {
104416
104408
  try {
@@ -104435,16 +104427,10 @@ var WalletService = class {
104435
104427
  const encryptedDevice = await LocalForage.get(
104436
104428
  STORAGE_KEYS.encryptedShare2Device(this.orgHost)
104437
104429
  );
104438
- if (!encryptedDevice) {
104439
- throw new SDKError(
104440
- "PIN reset is not available on this device",
104441
- "RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
104442
- );
104443
- }
104444
104430
  const deviceSecret = await LocalForage.get(
104445
104431
  STORAGE_KEYS.deviceSecret(this.orgHost)
104446
104432
  );
104447
- if (!deviceSecret) {
104433
+ if (!encryptedDevice || !deviceSecret) {
104448
104434
  throw new SDKError(
104449
104435
  "PIN reset is not available on this device",
104450
104436
  "RECOVERY_NOT_AVAILABLE" /* RECOVERY_NOT_AVAILABLE */
@@ -104461,7 +104447,10 @@ var WalletService = class {
104461
104447
  );
104462
104448
  }
104463
104449
  const walletInfo = await this.walletClient.getWallet();
104464
- const privateKey = await Secrets.combine([walletInfo.share1, share2]);
104450
+ const privateKey = await Secrets.combine([
104451
+ walletInfo.share1,
104452
+ share2
104453
+ ]);
104465
104454
  const wallet = new import_ethers2.Wallet(privateKey);
104466
104455
  const newShares = await Secrets.split(wallet.privateKey, 3, 2);
104467
104456
  const [newShare1, newShare2, newShare3] = newShares;
@@ -104477,15 +104466,7 @@ var WalletService = class {
104477
104466
  STORAGE_KEYS.encryptedShare2(this.orgHost),
104478
104467
  Crypto.encryptShare(newShare2, newPassword, firebaseId)
104479
104468
  );
104480
- const newEncryptedDevice = Crypto.encryptShare(
104481
- newShare2,
104482
- deviceSecret,
104483
- firebaseId
104484
- );
104485
- await LocalForage.save(
104486
- STORAGE_KEYS.encryptedShare2Device(this.orgHost),
104487
- newEncryptedDevice
104488
- );
104469
+ await this.ensureDeviceEncryptedShare2(newShare2, firebaseId);
104489
104470
  this.walletAddress = wallet.address;
104490
104471
  await LocalForage.save(
104491
104472
  STORAGE_KEYS.walletAddress(this.orgHost),
@@ -104494,7 +104475,6 @@ var WalletService = class {
104494
104475
  await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
104495
104476
  return wallet.address;
104496
104477
  } catch (error) {
104497
- console.error("Error in resetPin:", error);
104498
104478
  if (error instanceof SDKError) throw error;
104499
104479
  throw new SDKError(
104500
104480
  "Failed to reset PIN",
@@ -104532,38 +104512,20 @@ var WalletService = class {
104532
104512
  const rawBalance = await this.rpcClient.sendRpc({
104533
104513
  chainId,
104534
104514
  method: "eth_call" /* ETH_CALL */,
104535
- params: [
104536
- {
104537
- to: tokenAddress,
104538
- data
104539
- },
104540
- "latest"
104541
- ]
104515
+ params: [{ to: tokenAddress, data }, "latest"]
104542
104516
  });
104543
104517
  const decimalsData = erc20.encodeFunctionData("decimals", []);
104544
104518
  const decimalsRes = await this.rpcClient.sendRpc({
104545
104519
  chainId,
104546
104520
  method: "eth_call" /* ETH_CALL */,
104547
- params: [
104548
- {
104549
- to: tokenAddress,
104550
- data: decimalsData
104551
- },
104552
- "latest"
104553
- ]
104521
+ params: [{ to: tokenAddress, data: decimalsData }, "latest"]
104554
104522
  });
104555
104523
  const decimals = parseInt(decimalsRes.result, 16);
104556
104524
  const symbolData = erc20.encodeFunctionData("symbol", []);
104557
104525
  const symbolRes = await this.rpcClient.sendRpc({
104558
104526
  chainId,
104559
104527
  method: "eth_call" /* ETH_CALL */,
104560
- params: [
104561
- {
104562
- to: tokenAddress,
104563
- data: symbolData
104564
- },
104565
- "latest"
104566
- ]
104528
+ params: [{ to: tokenAddress, data: symbolData }, "latest"]
104567
104529
  });
104568
104530
  const symbol = erc20.decodeFunctionResult("symbol", symbolRes.result)[0];
104569
104531
  return {
@@ -104652,34 +104614,36 @@ var WalletService = class {
104652
104614
  method: "eth_getTransactionCount" /* ETH_GET_TRANSACTION_COUNT */,
104653
104615
  params: [address, "latest"]
104654
104616
  });
104655
- if (!nonce.result || nonce.result === "0x0") {
104656
- return void 0;
104657
- }
104617
+ if (!nonce.result || nonce.result === "0x0") return void 0;
104658
104618
  return {
104659
104619
  hash: "",
104660
104620
  status: "SUCCESS" /* SUCCESS */,
104661
104621
  timestamp: Date.now(),
104662
104622
  value: "0"
104663
104623
  };
104664
- } catch (error) {
104665
- console.error("Failed to get latest transaction:", error);
104624
+ } catch {
104666
104625
  return void 0;
104667
104626
  }
104668
104627
  }
104628
+ /**
104629
+ * PATCH APPLIED (critical):
104630
+ * - Never overwrite cached walletAddress with derived signer address.
104631
+ * - If server/cached address != derived address => throw WALLET_RECOVERY_FAILED.
104632
+ */
104669
104633
  async sendTransaction(params) {
104634
+ const toHexQuantity = (v5) => {
104635
+ if (v5 === void 0 || v5 === null) return "0x0";
104636
+ const s5 = String(v5).trim();
104637
+ if (!s5) return "0x0";
104638
+ if (s5.startsWith("0x") || s5.startsWith("0X")) return s5;
104639
+ try {
104640
+ const bi = BigInt(s5);
104641
+ return "0x" + bi.toString(16);
104642
+ } catch {
104643
+ return s5;
104644
+ }
104645
+ };
104670
104646
  try {
104671
- const toHexQuantity = (v5) => {
104672
- if (v5 === void 0 || v5 === null) return "0x0";
104673
- const s5 = String(v5).trim();
104674
- if (!s5) return "0x0";
104675
- if (s5.startsWith("0x") || s5.startsWith("0X")) return s5;
104676
- try {
104677
- const bi = BigInt(s5);
104678
- return "0x" + bi.toString(16);
104679
- } catch {
104680
- return s5;
104681
- }
104682
- };
104683
104647
  const walletInfo = await this.walletClient.getWallet();
104684
104648
  const share1 = walletInfo?.share1;
104685
104649
  let share2 = await LocalForage.get(
@@ -104716,25 +104680,25 @@ var WalletService = class {
104716
104680
  const privateKey = await Secrets.combine([share1, share2]);
104717
104681
  const wallet = new import_ethers2.Wallet(privateKey);
104718
104682
  const from = wallet.address;
104719
- const cachedAddress = await LocalForage.get(
104720
- STORAGE_KEYS.walletAddress(this.orgHost)
104721
- );
104722
- if (cachedAddress && cachedAddress.toLowerCase() !== from.toLowerCase()) {
104723
- console.warn(
104724
- "[WalletService] walletAddress mismatch detected. cached=",
104725
- cachedAddress,
104726
- "derived=",
104727
- from
104683
+ const serverAddress = String(walletInfo?.address ?? "").trim();
104684
+ const cachedAddress = String(
104685
+ await LocalForage.get(
104686
+ STORAGE_KEYS.walletAddress(this.orgHost)
104687
+ ) ?? ""
104688
+ ).trim();
104689
+ const mismatch = (a5, b4) => a5 && b4 && a5.toLowerCase() !== b4.toLowerCase();
104690
+ if (mismatch(serverAddress, from) || mismatch(cachedAddress, from)) {
104691
+ throw new SDKError(
104692
+ `Wallet share mismatch detected. server=${serverAddress || "N/A"} cached=${cachedAddress || "N/A"} derived=${from}. Please re-enter PIN (retrieveWallet/resetPin) to refresh shares.`,
104693
+ "WALLET_RECOVERY_FAILED" /* WALLET_RECOVERY_FAILED */
104728
104694
  );
104729
- this.walletAddress = from;
104730
- await LocalForage.save(STORAGE_KEYS.walletAddress(this.orgHost), from);
104731
104695
  }
104732
104696
  const nativeBal = await this.rpcClient.sendRpc({
104733
104697
  chainId: params.chainId,
104734
104698
  method: "eth_getBalance" /* ETH_GET_BALANCE */,
104735
104699
  params: [from, "latest"]
104736
104700
  });
104737
- if (!nativeBal?.result || BigInt(nativeBal.result) === BigInt(0)) {
104701
+ if (!nativeBal?.result || BigInt(nativeBal.result) === 0n) {
104738
104702
  throw new SDKError(
104739
104703
  `Insufficient native balance for gas. sender=${from} balance=${nativeBal?.result ?? "0x0"}`,
104740
104704
  "TRANSACTION_FAILED" /* TRANSACTION_FAILED */
@@ -104770,17 +104734,16 @@ var WalletService = class {
104770
104734
  );
104771
104735
  const buffered = Math.max(21e3, Math.ceil(est * 1.2));
104772
104736
  gasLimit = "0x" + buffered.toString(16);
104773
- } catch (e7) {
104737
+ } catch {
104774
104738
  const data = (params.data || "0x").toLowerCase();
104775
104739
  const isApprove = data.startsWith("0x095ea7b3");
104776
104740
  const fallback = isApprove ? 12e4 : data !== "0x" ? 8e5 : 21e3;
104777
104741
  gasLimit = "0x" + fallback.toString(16);
104778
104742
  }
104779
104743
  }
104780
- const value = params.value ?? "0";
104781
104744
  const signedTx = await wallet.signTransaction({
104782
104745
  to: params.to,
104783
- value,
104746
+ value: params.value ?? "0",
104784
104747
  data: params.data || "0x",
104785
104748
  chainId: params.chainId,
104786
104749
  nonce,
@@ -104791,12 +104754,10 @@ var WalletService = class {
104791
104754
  const parsed = import_ethers2.Transaction.from(signedTx);
104792
104755
  const derivedFrom = parsed?.from;
104793
104756
  if (derivedFrom && String(derivedFrom).toLowerCase() !== from.toLowerCase()) {
104794
- console.warn(
104795
- "[WalletService] signedTx sender mismatch. wallet=",
104796
- from,
104797
- "txFrom=",
104798
- derivedFrom
104799
- );
104757
+ console.warn("[WalletService] signedTx sender mismatch.", {
104758
+ walletFrom: from,
104759
+ txFrom: derivedFrom
104760
+ });
104800
104761
  }
104801
104762
  } catch {
104802
104763
  }
@@ -104804,9 +104765,8 @@ var WalletService = class {
104804
104765
  await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
104805
104766
  return txHash;
104806
104767
  } catch (error) {
104807
- if (error instanceof SDKError) {
104808
- throw error;
104809
- }
104768
+ await LocalForage.delete(STORAGE_KEYS.share2(this.orgHost));
104769
+ if (error instanceof SDKError) throw error;
104810
104770
  const msg = (() => {
104811
104771
  try {
104812
104772
  const anyErr = error;
@@ -104955,10 +104915,26 @@ var HttpClient = class {
104955
104915
  if (!accessToken) {
104956
104916
  throw new SDKError("No access token found", "AUTH_REQUIRED" /* AUTH_REQUIRED */);
104957
104917
  }
104918
+ ;
104958
104919
  headers["Authorization"] = `Bearer ${accessToken}`;
104959
104920
  }
104960
104921
  return headers;
104961
104922
  }
104923
+ async safeReadErrorDetails(response) {
104924
+ const contentType = response.headers.get("content-type") || "";
104925
+ try {
104926
+ if (contentType.includes("application/json")) {
104927
+ return await response.json();
104928
+ }
104929
+ } catch {
104930
+ }
104931
+ try {
104932
+ const text = await response.text();
104933
+ return text || void 0;
104934
+ } catch {
104935
+ return void 0;
104936
+ }
104937
+ }
104962
104938
  async request(method, path, data, config2 = {}) {
104963
104939
  const headers = await this.getHeaders(config2.needsAccessToken);
104964
104940
  const requestInit = {
@@ -104969,20 +104945,28 @@ var HttpClient = class {
104969
104945
  },
104970
104946
  credentials: config2.credentials
104971
104947
  };
104972
- if (data) {
104948
+ if (data !== void 0) {
104973
104949
  requestInit.body = JSON.stringify(data);
104974
104950
  }
104975
104951
  const response = await fetch(`${this.baseUrl}${path}`, requestInit);
104976
104952
  if (!response.ok) {
104953
+ if (config2.needsAccessToken && (response.status === 401 || response.status === 403)) {
104954
+ await LocalForage.delete(`${this.orgHost}:accessToken`);
104955
+ throw new SDKError(
104956
+ `Authentication required (status: ${response.status})`,
104957
+ "AUTH_REQUIRED" /* AUTH_REQUIRED */,
104958
+ await this.safeReadErrorDetails(response)
104959
+ );
104960
+ }
104977
104961
  throw new SDKError(
104978
104962
  `HTTP error! status: ${response.status}`,
104979
104963
  "REQUEST_FAILED" /* REQUEST_FAILED */,
104980
- await response.json()
104964
+ await this.safeReadErrorDetails(response)
104981
104965
  );
104982
104966
  }
104983
- if (response.status === 200 && response.headers.get("content-length") === "0") {
104984
- return void 0;
104985
- }
104967
+ if (response.status === 204) return void 0;
104968
+ const contentLength = response.headers.get("content-length");
104969
+ if (contentLength === "0") return void 0;
104986
104970
  try {
104987
104971
  return await response.json();
104988
104972
  } catch {