decane-connect-kit 2.24.0 → 2.25.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.cjs CHANGED
@@ -2416,6 +2416,54 @@ var import_browser = require("@simplewebauthn/browser");
2416
2416
 
2417
2417
  // src/social/attestation.ts
2418
2418
  var import_dcap_qvl = require("@phala/dcap-qvl");
2419
+
2420
+ // src/social/collateralCache.ts
2421
+ var CACHE_PREFIX = "decane:collateral:v1:";
2422
+ var COLLATERAL_TTL_MS = 12 * 60 * 60 * 1e3;
2423
+ function storage() {
2424
+ try {
2425
+ return typeof localStorage !== "undefined" ? localStorage : null;
2426
+ } catch {
2427
+ return null;
2428
+ }
2429
+ }
2430
+ function keyFor(teeUrl) {
2431
+ return `${CACHE_PREFIX}${teeUrl}`;
2432
+ }
2433
+ function readCollateral(teeUrl) {
2434
+ const store = storage();
2435
+ if (!store) return null;
2436
+ try {
2437
+ const raw = store.getItem(keyFor(teeUrl));
2438
+ if (!raw) return null;
2439
+ const entry = JSON.parse(raw);
2440
+ if (!entry?.at || Date.now() - entry.at > COLLATERAL_TTL_MS) {
2441
+ store.removeItem(keyFor(teeUrl));
2442
+ return null;
2443
+ }
2444
+ return entry.collateral ?? null;
2445
+ } catch {
2446
+ return null;
2447
+ }
2448
+ }
2449
+ function writeCollateral(teeUrl, collateral) {
2450
+ const store = storage();
2451
+ if (!store) return;
2452
+ try {
2453
+ store.setItem(keyFor(teeUrl), JSON.stringify({ at: Date.now(), collateral }));
2454
+ } catch {
2455
+ }
2456
+ }
2457
+ function dropCollateral(teeUrl) {
2458
+ const store = storage();
2459
+ if (!store) return;
2460
+ try {
2461
+ store.removeItem(keyFor(teeUrl));
2462
+ } catch {
2463
+ }
2464
+ }
2465
+
2466
+ // src/social/attestation.ts
2419
2467
  init_errors();
2420
2468
 
2421
2469
  // ../../infra/tee-measurement.json
@@ -2443,6 +2491,9 @@ function hexToBytes(hex) {
2443
2491
  function bytesToHex(bytes) {
2444
2492
  return Array.from(bytes).map((b) => b.toString(16).padStart(2, "0")).join("");
2445
2493
  }
2494
+ function nowSeconds() {
2495
+ return Math.floor(Date.now() / 1e3);
2496
+ }
2446
2497
  async function verifyTeeAttestation(teeUrl, nonce) {
2447
2498
  const res = await fetch(`${teeUrl}/attestation`, {
2448
2499
  method: "POST",
@@ -2452,9 +2503,22 @@ async function verifyTeeAttestation(teeUrl, nonce) {
2452
2503
  if (!res.ok) throw new AttestationError("quote fetch failed");
2453
2504
  const { quote, measurement } = await res.json();
2454
2505
  let verified;
2506
+ const cached = readCollateral(teeUrl);
2455
2507
  try {
2456
- verified = await (0, import_dcap_qvl.getCollateralAndVerify)(hexToBytes(quote));
2457
- } catch {
2508
+ if (cached) {
2509
+ try {
2510
+ verified = await (0, import_dcap_qvl.verify)(hexToBytes(quote), cached, nowSeconds());
2511
+ } catch {
2512
+ dropCollateral(teeUrl);
2513
+ verified = await (0, import_dcap_qvl.getCollateralAndVerify)(hexToBytes(quote));
2514
+ }
2515
+ } else {
2516
+ const collateral = await (0, import_dcap_qvl.getCollateral)(import_dcap_qvl.PHALA_PCCS_URL, hexToBytes(quote));
2517
+ verified = await (0, import_dcap_qvl.verify)(hexToBytes(quote), collateral, nowSeconds());
2518
+ writeCollateral(teeUrl, collateral);
2519
+ }
2520
+ } catch (e) {
2521
+ if (e instanceof AttestationError) throw e;
2458
2522
  throw new AttestationError("quote signature invalid");
2459
2523
  }
2460
2524
  if (verified.status === "Revoked") {
@@ -2496,14 +2560,51 @@ async function teeFetch(url, init) {
2496
2560
  }
2497
2561
  var ATTESTATION_TTL_MS = 10 * 6e4;
2498
2562
  var attestationCache = /* @__PURE__ */ new Map();
2563
+ var ATTESTATION_PERSIST_PREFIX = "decane:attested:v1:";
2564
+ function persistedAt(teeUrl) {
2565
+ try {
2566
+ if (typeof sessionStorage === "undefined") return null;
2567
+ const raw = sessionStorage.getItem(ATTESTATION_PERSIST_PREFIX + teeUrl);
2568
+ const at = raw ? Number(raw) : NaN;
2569
+ return Number.isFinite(at) ? at : null;
2570
+ } catch {
2571
+ return null;
2572
+ }
2573
+ }
2574
+ function rememberAttested(teeUrl, at) {
2575
+ try {
2576
+ if (typeof sessionStorage !== "undefined") {
2577
+ sessionStorage.setItem(ATTESTATION_PERSIST_PREFIX + teeUrl, String(at));
2578
+ }
2579
+ } catch {
2580
+ }
2581
+ }
2582
+ function forgetAttested(teeUrl) {
2583
+ try {
2584
+ if (typeof sessionStorage !== "undefined") {
2585
+ sessionStorage.removeItem(ATTESTATION_PERSIST_PREFIX + teeUrl);
2586
+ }
2587
+ } catch {
2588
+ }
2589
+ }
2499
2590
  function attestOnce(teeUrl, verify) {
2500
2591
  const hit = attestationCache.get(teeUrl);
2501
2592
  if (hit && Date.now() - hit.at < ATTESTATION_TTL_MS) return hit.promise;
2502
- const promise = verify().catch((e) => {
2593
+ const at = persistedAt(teeUrl);
2594
+ if (at && Date.now() - at < ATTESTATION_TTL_MS) {
2595
+ const resolved = Promise.resolve();
2596
+ attestationCache.set(teeUrl, { at, promise: resolved });
2597
+ return resolved;
2598
+ }
2599
+ const startedAt = Date.now();
2600
+ const promise = verify().then(() => {
2601
+ rememberAttested(teeUrl, startedAt);
2602
+ }).catch((e) => {
2503
2603
  attestationCache.delete(teeUrl);
2604
+ forgetAttested(teeUrl);
2504
2605
  throw e;
2505
2606
  });
2506
- attestationCache.set(teeUrl, { at: Date.now(), promise });
2607
+ attestationCache.set(teeUrl, { at: startedAt, promise });
2507
2608
  return promise;
2508
2609
  }
2509
2610
  function prewarmAttestation(teeUrl) {
@@ -3232,6 +3333,8 @@ function loginWithKingsChatPopup(opts) {
3232
3333
 
3233
3334
  // src/social/signers/evm.ts
3234
3335
  var import_ethers2 = require("ethers");
3336
+ init_errors();
3337
+ var DECODE = (bytes) => new TextDecoder().decode(bytes);
3235
3338
  async function signEvmTransaction(tee, chain, tx, jwt) {
3236
3339
  const resolved = await (0, import_ethers2.resolveProperties)(tx);
3237
3340
  const unsigned = import_ethers2.Transaction.from(resolved).unsignedSerialized;
@@ -3240,7 +3343,44 @@ async function signEvmTransaction(tee, chain, tx, jwt) {
3240
3343
  return tee.sign({ chain, kind: "transaction", payload, txHash }, jwt);
3241
3344
  }
3242
3345
  async function signEvmMessage(tee, chain, message, jwt) {
3243
- return tee.sign({ chain, kind: "message", payload: message }, jwt);
3346
+ return tee.sign({ chain, kind: "message", payload: messageAsString(message) }, jwt);
3347
+ }
3348
+ function messageAsString(message) {
3349
+ if (typeof message === "string") return message;
3350
+ if (!isValidUtf8(message)) {
3351
+ throw new CallSequenceError(
3352
+ "signMessage: the message bytes are not valid UTF-8. The signer hashes text; pass a string, or UTF-8 bytes."
3353
+ );
3354
+ }
3355
+ return DECODE(message);
3356
+ }
3357
+ function isValidUtf8(b) {
3358
+ let i = 0;
3359
+ while (i < b.length) {
3360
+ const c = b[i];
3361
+ let n = 0;
3362
+ if (c < 128) n = 0;
3363
+ else if (c >= 194 && c <= 223) n = 1;
3364
+ else if (c >= 224 && c <= 239) n = 2;
3365
+ else if (c >= 240 && c <= 244) n = 3;
3366
+ else return false;
3367
+ for (let k = 1; k <= n; k++) {
3368
+ const t = b[i + k];
3369
+ if (t === void 0 || (t & 192) !== 128) return false;
3370
+ }
3371
+ if (n === 2) {
3372
+ const c1 = b[i + 1];
3373
+ if (c === 224 && c1 < 160) return false;
3374
+ if (c === 237 && c1 >= 160) return false;
3375
+ }
3376
+ if (n === 3) {
3377
+ const c1 = b[i + 1];
3378
+ if (c === 240 && c1 < 144) return false;
3379
+ if (c === 244 && c1 >= 144) return false;
3380
+ }
3381
+ i += n + 1;
3382
+ }
3383
+ return true;
3244
3384
  }
3245
3385
  async function signEvmAuthorization(tee, chainId, address, nonce, jwt) {
3246
3386
  const serialized = await tee.sign(
@@ -3736,29 +3876,44 @@ function clearLastUser(key) {
3736
3876
  function sessionPersistKey(apiKey) {
3737
3877
  return `decane:sess:${apiKey.slice(0, 20)}`;
3738
3878
  }
3879
+ var sessionStore = () => typeof sessionStorage !== "undefined" ? sessionStorage : null;
3880
+ function useCrossTabSessionStore(enabled) {
3881
+ sessionStore = enabled ? () => typeof localStorage !== "undefined" ? localStorage : null : () => typeof sessionStorage !== "undefined" ? sessionStorage : null;
3882
+ }
3739
3883
  function loadSession(key) {
3740
3884
  try {
3741
- const raw = typeof sessionStorage !== "undefined" ? sessionStorage.getItem(key) : null;
3742
- return raw ? JSON.parse(raw) : null;
3885
+ const raw = sessionStore()?.getItem(key) ?? (typeof sessionStorage !== "undefined" ? sessionStorage.getItem(key) : null) ?? (typeof localStorage !== "undefined" ? localStorage.getItem(key) : null);
3886
+ if (!raw) return null;
3887
+ const parsed = JSON.parse(raw);
3888
+ if (parsed?.expiresAt && parsed.expiresAt <= Date.now()) {
3889
+ clearSessionPersist(key);
3890
+ return null;
3891
+ }
3892
+ return parsed;
3743
3893
  } catch {
3744
3894
  return null;
3745
3895
  }
3746
3896
  }
3747
3897
  function saveSession(key, s) {
3748
3898
  try {
3749
- sessionStorage.setItem(key, JSON.stringify(s));
3899
+ sessionStore()?.setItem(key, JSON.stringify(s));
3750
3900
  } catch {
3751
3901
  }
3752
3902
  }
3753
3903
  function clearSessionPersist(key) {
3754
3904
  try {
3755
- sessionStorage.removeItem(key);
3905
+ if (typeof sessionStorage !== "undefined") sessionStorage.removeItem(key);
3906
+ } catch {
3907
+ }
3908
+ try {
3909
+ if (typeof localStorage !== "undefined") localStorage.removeItem(key);
3756
3910
  } catch {
3757
3911
  }
3758
3912
  }
3759
3913
  async function createDecaneConnect(config) {
3760
3914
  if (!config.appId) throw new Error("decane-connect-kit: appId is required");
3761
3915
  if (!config.apiKey) throw new Error("decane-connect-kit: apiKey is required");
3916
+ useCrossTabSessionStore(config.resumeSessionAcrossTabs === true);
3762
3917
  if (!config.chains?.length)
3763
3918
  throw new Error("decane-connect-kit: at least one chain is required");
3764
3919
  if (config.rotateOnNewDeviceRecovery && !config.onRecoveryRotated) {
@@ -3777,7 +3932,7 @@ async function createDecaneConnect(config) {
3777
3932
  const teeUrl = config.teeUrl ?? DEFAULT_TEE_URL;
3778
3933
  const events = new EventEmitter();
3779
3934
  const api = new ApiClient(baseUrl, config.apiKey, config.appId);
3780
- const storage = new Storage();
3935
+ const storage2 = new Storage();
3781
3936
  const session = new SessionManager(
3782
3937
  events,
3783
3938
  durationMs,
@@ -3920,7 +4075,7 @@ async function createDecaneConnect(config) {
3920
4075
  }
3921
4076
  async function attachUnlockToken(userId, jwt, tokenKey) {
3922
4077
  try {
3923
- const stored = await storage.getShare(userId);
4078
+ const stored = await storage2.getShare(userId);
3924
4079
  if (!stored) return;
3925
4080
  const { token } = await api.issueUnlockToken(jwt);
3926
4081
  const unlockTokenIv = generateRandomBytes(12);
@@ -3929,13 +4084,13 @@ async function createDecaneConnect(config) {
3929
4084
  new TextEncoder().encode(token),
3930
4085
  unlockTokenIv
3931
4086
  );
3932
- await storage.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
4087
+ await storage2.putShare({ ...stored, wrappedUnlockToken, unlockTokenIv });
3933
4088
  } catch (e) {
3934
4089
  console.warn("decane-connect-kit: could not store an unlock token for this device.", e);
3935
4090
  }
3936
4091
  }
3937
4092
  async function persistRotatedDeviceShare(userId, deviceShare) {
3938
- const stored = await storage.getShare(userId);
4093
+ const stored = await storage2.getShare(userId);
3939
4094
  if (!stored) {
3940
4095
  throw new CallSequenceError(
3941
4096
  "Share set was rotated but this device has no stored share to update. Recover the wallet on this device before rotating."
@@ -3944,21 +4099,21 @@ async function createDecaneConnect(config) {
3944
4099
  const wrappingKey = await getWrappingKey(stored);
3945
4100
  const iv = generateRandomBytes(12);
3946
4101
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
3947
- await storage.putShare({ ...stored, wrappedDeviceShare, iv });
4102
+ await storage2.putShare({ ...stored, wrappedDeviceShare, iv });
3948
4103
  }
3949
4104
  function hasPasswordUnlock(stored) {
3950
4105
  return !!stored && stored.unlockMethod === "password" && !!stored.passwordSalt && !!stored.wrappedUnlockToken && !!stored.unlockTokenIv;
3951
4106
  }
3952
4107
  async function passwordDeviceCandidate() {
3953
4108
  if (persisted?.userId) {
3954
- const stored = await storage.getShare(persisted.userId);
4109
+ const stored = await storage2.getShare(persisted.userId);
3955
4110
  return hasPasswordUnlock(stored) ? stored : null;
3956
4111
  }
3957
4112
  if (lastUserId) {
3958
- const stored = await storage.getShare(lastUserId);
4113
+ const stored = await storage2.getShare(lastUserId);
3959
4114
  return hasPasswordUnlock(stored) ? stored : null;
3960
4115
  }
3961
- const candidates = (await storage.listShares()).filter(hasPasswordUnlock);
4116
+ const candidates = (await storage2.listShares()).filter(hasPasswordUnlock);
3962
4117
  return candidates.length === 1 ? candidates[0] : null;
3963
4118
  }
3964
4119
  async function getWrappingKey(stored) {
@@ -3986,7 +4141,7 @@ async function createDecaneConnect(config) {
3986
4141
  let credentialId;
3987
4142
  let prfOutput;
3988
4143
  try {
3989
- const label = (await storage.getShare(userId))?.profile?.email;
4144
+ const label = (await storage2.getShare(userId))?.profile?.email;
3990
4145
  ({ credentialId, prfOutput } = await registerWebAuthnCredential({
3991
4146
  api,
3992
4147
  jwt,
@@ -4018,8 +4173,8 @@ async function createDecaneConnect(config) {
4018
4173
  const wrappingKey = await importAesKey(prfOutput);
4019
4174
  const iv = generateRandomBytes(12);
4020
4175
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4021
- const existing = await storage.getShare(userId);
4022
- await storage.putShare({
4176
+ const existing = await storage2.getShare(userId);
4177
+ await storage2.putShare({
4023
4178
  ...existing,
4024
4179
  userId,
4025
4180
  wrappedDeviceShare,
@@ -4103,7 +4258,7 @@ async function createDecaneConnect(config) {
4103
4258
  const { wrappingKey, tokenKey, meta } = await passwordSetup();
4104
4259
  const iv = generateRandomBytes(12);
4105
4260
  const wrappedDeviceShare = await aesGcmEncrypt(wrappingKey, deviceShare, iv);
4106
- await storage.putShare({
4261
+ await storage2.putShare({
4107
4262
  userId,
4108
4263
  wrappedDeviceShare,
4109
4264
  iv,
@@ -4177,7 +4332,7 @@ async function createDecaneConnect(config) {
4177
4332
  shareSet.device,
4178
4333
  iv
4179
4334
  );
4180
- await storage.putShare({
4335
+ await storage2.putShare({
4181
4336
  userId,
4182
4337
  wrappedDeviceShare,
4183
4338
  iv,
@@ -4281,7 +4436,7 @@ async function createDecaneConnect(config) {
4281
4436
  async function returningUserUnlock(userId, jwt) {
4282
4437
  const rawAddresses = await api.getAddresses(jwt);
4283
4438
  const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
4284
- const stored = await storage.getShare(userId);
4439
+ const stored = await storage2.getShare(userId);
4285
4440
  let deviceShare;
4286
4441
  let joinedViaPasskeySync = false;
4287
4442
  if (!stored) {
@@ -4320,7 +4475,7 @@ async function createDecaneConnect(config) {
4320
4475
  rpId,
4321
4476
  teeUrl,
4322
4477
  api,
4323
- storage,
4478
+ storage: storage2,
4324
4479
  onProgress: config.onRecoveryProgress,
4325
4480
  promptForRecoveryFile: config.promptForRecoveryFile,
4326
4481
  onNewRecoveryShare: async (recoveryShare, shareSetVersion) => {
@@ -4334,7 +4489,7 @@ async function createDecaneConnect(config) {
4334
4489
  });
4335
4490
  deviceShare = recovered.deviceShare;
4336
4491
  joinedViaPasskeySync = recovered.flow === "passkey-sync";
4337
- if (!deferDeviceProtection && !await storage.getShare(userId) && isPasskeySupported()) {
4492
+ if (!deferDeviceProtection && !await storage2.getShare(userId) && isPasskeySupported()) {
4338
4493
  await registerPasskeyAndBackup(userId, jwt, deviceShare);
4339
4494
  }
4340
4495
  } else {
@@ -4355,7 +4510,7 @@ async function createDecaneConnect(config) {
4355
4510
  } catch (e) {
4356
4511
  if (e instanceof StaleDeviceShareError) {
4357
4512
  zeroize(deviceShare);
4358
- await storage.deleteShare(userId);
4513
+ await storage2.deleteShare(userId);
4359
4514
  if (stored) return returningUserUnlock(userId, jwt);
4360
4515
  } else {
4361
4516
  zeroize(deviceShare);
@@ -4379,7 +4534,7 @@ async function createDecaneConnect(config) {
4379
4534
  const result = await tee.rotateShares(jwt);
4380
4535
  try {
4381
4536
  await persistRotatedDeviceShare(userId, result.deviceShare);
4382
- const refreshed = await storage.getShare(userId);
4537
+ const refreshed = await storage2.getShare(userId);
4383
4538
  if (refreshed?.unlockMethod === "passkey" && refreshed.credentialId && refreshed.prfSalt) {
4384
4539
  await api.putRecoveryBackup(jwt, {
4385
4540
  wrappedDeviceShare: refreshed.wrappedDeviceShare,
@@ -4412,13 +4567,13 @@ async function createDecaneConnect(config) {
4412
4567
  }
4413
4568
  async function passkeyLoginCandidate() {
4414
4569
  if (!persisted?.userId || !isPasskeySupported()) return null;
4415
- return usablePasskeyShare(await storage.getShare(persisted.userId));
4570
+ return usablePasskeyShare(await storage2.getShare(persisted.userId));
4416
4571
  }
4417
4572
  async function passkeyDeviceCandidate() {
4418
4573
  if (!isPasskeySupported()) return null;
4419
4574
  if (persisted?.userId) return passkeyLoginCandidate();
4420
- if (lastUserId) return usablePasskeyShare(await storage.getShare(lastUserId));
4421
- const candidates = (await storage.listShares()).map(usablePasskeyShare).filter(Boolean);
4575
+ if (lastUserId) return usablePasskeyShare(await storage2.getShare(lastUserId));
4576
+ const candidates = (await storage2.listShares()).map(usablePasskeyShare).filter(Boolean);
4422
4577
  return candidates.length === 1 ? candidates[0] : null;
4423
4578
  }
4424
4579
  async function canOfferPasskey() {
@@ -4426,15 +4581,15 @@ async function createDecaneConnect(config) {
4426
4581
  if (await passkeyDeviceCandidate()) return true;
4427
4582
  const known = persisted?.userId ?? lastUserId;
4428
4583
  if (!known) return false;
4429
- return await storage.getShare(known) == null;
4584
+ return await storage2.getShare(known) == null;
4430
4585
  }
4431
4586
  async function rememberProfile(userId, profile) {
4432
4587
  const incoming = Object.fromEntries(Object.entries(profile).filter(([, v]) => v));
4433
4588
  if (!Object.keys(incoming).length) return;
4434
4589
  try {
4435
- const stored = await storage.getShare(userId);
4590
+ const stored = await storage2.getShare(userId);
4436
4591
  if (!stored) return;
4437
- await storage.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4592
+ await storage2.putShare({ ...stored, profile: { ...stored.profile, ...incoming } });
4438
4593
  } catch {
4439
4594
  }
4440
4595
  }
@@ -4458,7 +4613,7 @@ async function createDecaneConnect(config) {
4458
4613
  savePersisted(PERSIST_KEY, persisted);
4459
4614
  events.emit("connected");
4460
4615
  persistLiveSession();
4461
- const stored = await storage.getShare(userId);
4616
+ const stored = await storage2.getShare(userId);
4462
4617
  return {
4463
4618
  addresses,
4464
4619
  isNewUser: false,
@@ -4631,7 +4786,7 @@ async function createDecaneConnect(config) {
4631
4786
  events.emit("connected");
4632
4787
  persistLiveSession();
4633
4788
  scheduleAccessTokenExpiry(token, claims.expiresAt);
4634
- const stored = await storage.getShare(claims.userId);
4789
+ const stored = await storage2.getShare(claims.userId);
4635
4790
  return {
4636
4791
  addresses,
4637
4792
  isNewUser: false,
@@ -4661,7 +4816,7 @@ async function createDecaneConnect(config) {
4661
4816
  const candidate = await passkeyDeviceCandidate();
4662
4817
  if (candidate) return passkeyLogin(candidate);
4663
4818
  const known = persisted?.userId ?? lastUserId;
4664
- if (isPasskeySupported() && known && await storage.getShare(known) == null) {
4819
+ if (isPasskeySupported() && known && await storage2.getShare(known) == null) {
4665
4820
  return passkeyLoginWithoutShare(known);
4666
4821
  }
4667
4822
  throw new NewDeviceError();
@@ -4680,7 +4835,7 @@ async function createDecaneConnect(config) {
4680
4835
  async setUnlockPassword(password) {
4681
4836
  if (!auth) throw new ReconnectRequiredError();
4682
4837
  validateUnlockPassword(password);
4683
- const stored = await storage.getShare(auth.userId);
4838
+ const stored = await storage2.getShare(auth.userId);
4684
4839
  if (!stored) throw new NewDeviceError();
4685
4840
  const deviceShare = await aesGcmDecrypt(
4686
4841
  await getWrappingKey(stored),
@@ -4699,7 +4854,7 @@ async function createDecaneConnect(config) {
4699
4854
  );
4700
4855
  const iv = generateRandomBytes(12);
4701
4856
  const wrappedDeviceShare = await aesGcmEncrypt(shareKey, deviceShare, iv);
4702
- await storage.putShare({
4857
+ await storage2.putShare({
4703
4858
  ...stored,
4704
4859
  unlockMethod: "password",
4705
4860
  wrappedDeviceShare,
@@ -4779,9 +4934,9 @@ async function createDecaneConnect(config) {
4779
4934
  async removeUnlockPassword() {
4780
4935
  if (!auth) throw new ReconnectRequiredError();
4781
4936
  await api.revokeUnlockTokens(auth.jwt);
4782
- const stored = await storage.getShare(auth.userId);
4937
+ const stored = await storage2.getShare(auth.userId);
4783
4938
  if (!stored || stored.unlockMethod !== "password") return;
4784
- await storage.putShare({
4939
+ await storage2.putShare({
4785
4940
  ...stored,
4786
4941
  wrappedUnlockToken: void 0,
4787
4942
  unlockTokenIv: void 0
@@ -4789,13 +4944,13 @@ async function createDecaneConnect(config) {
4789
4944
  },
4790
4945
  async hasPasskey() {
4791
4946
  if (!auth) return false;
4792
- const stored = await storage.getShare(auth.userId);
4947
+ const stored = await storage2.getShare(auth.userId);
4793
4948
  return stored?.unlockMethod === "passkey";
4794
4949
  },
4795
4950
  async addPasskey() {
4796
4951
  if (!auth) throw new ReconnectRequiredError();
4797
4952
  if (!isPasskeySupported()) throw new WebAuthnNotSupportedError();
4798
- const stored = await storage.getShare(auth.userId);
4953
+ const stored = await storage2.getShare(auth.userId);
4799
4954
  if (stored?.unlockMethod === "passkey") return;
4800
4955
  if (!stored) {
4801
4956
  if (!serverAssistedRecovery) throw new NewDeviceError();
@@ -4828,11 +4983,11 @@ async function createDecaneConnect(config) {
4828
4983
  },
4829
4984
  async deviceProtected() {
4830
4985
  if (!auth) return false;
4831
- return await storage.getShare(auth.userId) !== null;
4986
+ return await storage2.getShare(auth.userId) !== null;
4832
4987
  },
4833
4988
  async protectDevice() {
4834
4989
  if (!auth) throw new ReconnectRequiredError();
4835
- if (await storage.getShare(auth.userId)) return true;
4990
+ if (await storage2.getShare(auth.userId)) return true;
4836
4991
  if (!serverAssistedRecovery) throw new NewDeviceError();
4837
4992
  const tee = new TeeClient(teeUrl);
4838
4993
  const provisioned = await tee.provisionDeviceShare(auth.jwt);
@@ -4842,7 +4997,7 @@ async function createDecaneConnect(config) {
4842
4997
  } finally {
4843
4998
  zeroize(provisioned);
4844
4999
  }
4845
- return await storage.getShare(auth.userId) !== null;
5000
+ return await storage2.getShare(auth.userId) !== null;
4846
5001
  },
4847
5002
  async unlock() {
4848
5003
  if (!auth) {
@@ -4884,8 +5039,7 @@ async function createDecaneConnect(config) {
4884
5039
  return bufferToB64u(sig);
4885
5040
  }
4886
5041
  if (req.chain.startsWith("tron:")) {
4887
- const msg = typeof req.message === "string" ? req.message : new TextDecoder().decode(req.message);
4888
- return tee.sign({ chain: req.chain, kind: "message", payload: msg }, auth?.jwt);
5042
+ return tee.sign({ chain: req.chain, kind: "message", payload: messageAsString(req.message) }, auth?.jwt);
4889
5043
  }
4890
5044
  throw new CallSequenceError(`Unsupported chain: ${req.chain}`);
4891
5045
  },