@tatchi-xyz/sdk 0.17.0 → 0.19.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.
Files changed (162) hide show
  1. package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
  2. package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  3. package/dist/cjs/core/EmailRecovery/index.js +32 -20
  4. package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
  5. package/dist/cjs/core/TatchiPasskey/emailRecovery.js +507 -452
  6. package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
  7. package/dist/cjs/core/TatchiPasskey/index.js +1 -0
  8. package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
  9. package/dist/cjs/core/TatchiPasskey/relay.js +23 -1
  10. package/dist/cjs/core/TatchiPasskey/relay.js.map +1 -1
  11. package/dist/cjs/core/WalletIframe/client/IframeTransport.js +0 -7
  12. package/dist/cjs/core/WalletIframe/client/IframeTransport.js.map +1 -1
  13. package/dist/cjs/core/WalletIframe/client/router.js +6 -2
  14. package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
  15. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
  16. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  17. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
  18. package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  19. package/dist/cjs/core/WebAuthnManager/index.js +23 -0
  20. package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
  21. package/dist/cjs/core/rpcCalls.js +8 -0
  22. package/dist/cjs/core/rpcCalls.js.map +1 -1
  23. package/dist/cjs/index.js +6 -2
  24. package/dist/cjs/index.js.map +1 -1
  25. package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
  26. package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
  27. package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
  28. package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
  29. package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
  30. package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
  31. package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
  32. package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
  33. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
  34. package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
  35. package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
  36. package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
  37. package/dist/cjs/react/hooks/usePreconnectWalletAssets.js +27 -32
  38. package/dist/cjs/react/hooks/usePreconnectWalletAssets.js.map +1 -1
  39. package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
  40. package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  41. package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +32 -20
  42. package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
  43. package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +507 -452
  44. package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
  45. package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +1 -0
  46. package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
  47. package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
  48. package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
  49. package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
  50. package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
  51. package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +6 -2
  52. package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
  53. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
  54. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  55. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
  56. package/dist/cjs/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  57. package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js +23 -0
  58. package/dist/cjs/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
  59. package/dist/cjs/react/sdk/src/core/rpcCalls.js +8 -0
  60. package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
  61. package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
  62. package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  63. package/dist/esm/core/EmailRecovery/index.js +28 -21
  64. package/dist/esm/core/EmailRecovery/index.js.map +1 -1
  65. package/dist/esm/core/TatchiPasskey/emailRecovery.js +507 -452
  66. package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
  67. package/dist/esm/core/TatchiPasskey/index.js +2 -1
  68. package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
  69. package/dist/esm/core/TatchiPasskey/relay.js +23 -1
  70. package/dist/esm/core/TatchiPasskey/relay.js.map +1 -1
  71. package/dist/esm/core/WalletIframe/client/IframeTransport.js +0 -7
  72. package/dist/esm/core/WalletIframe/client/IframeTransport.js.map +1 -1
  73. package/dist/esm/core/WalletIframe/client/router.js +7 -3
  74. package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
  75. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
  76. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  77. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
  78. package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  79. package/dist/esm/core/WebAuthnManager/index.js +23 -0
  80. package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
  81. package/dist/esm/core/rpcCalls.js +8 -1
  82. package/dist/esm/core/rpcCalls.js.map +1 -1
  83. package/dist/esm/index.js +4 -1
  84. package/dist/esm/index.js.map +1 -1
  85. package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
  86. package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
  87. package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
  88. package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
  89. package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
  90. package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
  91. package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
  92. package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
  93. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
  94. package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
  95. package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
  96. package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
  97. package/dist/esm/react/hooks/usePreconnectWalletAssets.js +27 -32
  98. package/dist/esm/react/hooks/usePreconnectWalletAssets.js.map +1 -1
  99. package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
  100. package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
  101. package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +28 -21
  102. package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
  103. package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +507 -452
  104. package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
  105. package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
  106. package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
  107. package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
  108. package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
  109. package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
  110. package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
  111. package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +7 -3
  112. package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
  113. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js +1 -1
  114. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.js.map +1 -1
  115. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js +1 -1
  116. package/dist/esm/react/sdk/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/localOnly.js.map +1 -1
  117. package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js +23 -0
  118. package/dist/esm/react/sdk/src/core/WebAuthnManager/index.js.map +1 -1
  119. package/dist/esm/react/sdk/src/core/rpcCalls.js +8 -1
  120. package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
  121. package/dist/esm/sdk/{createAdapters-qVGD6i0g.js → createAdapters-DIRR8_Z9.js} +1 -1
  122. package/dist/esm/sdk/{createAdapters-BumKM2ft.js → createAdapters-Yga6W0en.js} +2 -2
  123. package/dist/esm/sdk/{createAdapters-BumKM2ft.js.map → createAdapters-Yga6W0en.js.map} +1 -1
  124. package/dist/esm/sdk/{localOnly-pXMTqh1m.js → localOnly-BHScJasw.js} +2 -2
  125. package/dist/esm/sdk/{localOnly-Byi3AK7A.js → localOnly-VevCI7H0.js} +3 -3
  126. package/dist/esm/sdk/{localOnly-Byi3AK7A.js.map → localOnly-VevCI7H0.js.map} +1 -1
  127. package/dist/esm/sdk/offline-export-app.js +29 -6
  128. package/dist/esm/sdk/offline-export-app.js.map +1 -1
  129. package/dist/esm/sdk/{registration-CBiS4Ua_.js → registration-bKEg9Zr2.js} +2 -2
  130. package/dist/esm/sdk/{registration-CBiS4Ua_.js.map → registration-bKEg9Zr2.js.map} +1 -1
  131. package/dist/esm/sdk/{registration-DLPLsGCz.js → registration-lDD60Ytt.js} +1 -1
  132. package/dist/esm/sdk/{router-BLFegW7J.js → router-DuGYOd3G.js} +6 -9
  133. package/dist/esm/sdk/{rpcCalls-DEv9x5-f.js → rpcCalls-BQrJMTdg.js} +2 -2
  134. package/dist/esm/sdk/{rpcCalls-OhgEeFig.js → rpcCalls-YVeUVMk2.js} +8 -1
  135. package/dist/esm/sdk/{transactions-Bk-VavcV.js → transactions-BalIhtJ9.js} +1 -1
  136. package/dist/esm/sdk/{transactions-BIqKZeR0.js → transactions-bqaAwL4k.js} +2 -2
  137. package/dist/esm/sdk/{transactions-BIqKZeR0.js.map → transactions-bqaAwL4k.js.map} +1 -1
  138. package/dist/esm/sdk/wallet-iframe-host.js +641 -481
  139. package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
  140. package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts +25 -0
  141. package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts.map +1 -0
  142. package/dist/types/src/core/EmailRecovery/index.d.ts +1 -0
  143. package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
  144. package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +38 -6
  145. package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
  146. package/dist/types/src/core/TatchiPasskey/index.d.ts +2 -2
  147. package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
  148. package/dist/types/src/core/TatchiPasskey/relay.d.ts +2 -1
  149. package/dist/types/src/core/TatchiPasskey/relay.d.ts.map +1 -1
  150. package/dist/types/src/core/WalletIframe/client/IframeTransport.d.ts.map +1 -1
  151. package/dist/types/src/core/WalletIframe/client/router.d.ts +3 -3
  152. package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
  153. package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/webauthn.d.ts.map +1 -1
  154. package/dist/types/src/core/WebAuthnManager/index.d.ts +7 -0
  155. package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
  156. package/dist/types/src/core/rpcCalls.d.ts +9 -0
  157. package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
  158. package/dist/types/src/index.d.ts +1 -0
  159. package/dist/types/src/index.d.ts.map +1 -1
  160. package/dist/types/src/react/hooks/usePreconnectWalletAssets.d.ts.map +1 -1
  161. package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
  162. package/package.json +1 -1
@@ -15,7 +15,7 @@ import { MinimalNearClient, SignedTransaction, isOffline, openOfflineExport } fr
15
15
  import { AccountRecoveryPhase, AccountRecoveryStatus, ActionPhase, ActionStatus, DEFAULT_WAIT_STATUS, DeviceLinkingPhase, DeviceLinkingStatus, EmailRecoveryPhase, EmailRecoveryStatus, LoginPhase, LoginStatus, RegistrationPhase, RegistrationStatus, init_rpc, init_sdkSentEvents } from "./sdkSentEvents-_jrJLIhw.js";
16
16
  import { SecureConfirmMessageType, SecureConfirmationType, TouchIdPrompt, authenticatorsToAllowCredentials, createRandomVRFChallenge, getIntentDigest, init_accountIds, init_touchIdPrompt, init_vrf_worker, parseTransactionSummary, sanitizeForPostMessage, sendConfirmResponse, toAccountId, validateVRFChallenge } from "./requestHelpers-Dh1hEYL9.js";
17
17
  import { PASSKEY_MANAGER_DEFAULT_CONFIGS, buildConfigsFromEnv, init_defaultConfigs } from "./defaultConfigs-DpslkAQd.js";
18
- import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-OhgEeFig.js";
18
+ import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-YVeUVMk2.js";
19
19
  import { getLastLoggedInDeviceNumber, init_getDeviceNumber, parseDeviceNumber } from "./getDeviceNumber-zsOHT_Um.js";
20
20
 
21
21
  //#region src/core/sdkPaths/workers.ts
@@ -3271,42 +3271,42 @@ async function importFlow(label, loader) {
3271
3271
  }
3272
3272
  const HANDLERS = {
3273
3273
  [SecureConfirmationType.DECRYPT_PRIVATE_KEY_WITH_PRF]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3274
- const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-pXMTqh1m.js"));
3274
+ const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-BHScJasw.js"));
3275
3275
  await handleLocalOnlyFlow(ctx, request, worker, {
3276
3276
  confirmationConfig,
3277
3277
  transactionSummary
3278
3278
  });
3279
3279
  },
3280
3280
  [SecureConfirmationType.SHOW_SECURE_PRIVATE_KEY_UI]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3281
- const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-pXMTqh1m.js"));
3281
+ const { handleLocalOnlyFlow } = await importFlow("localOnly", () => import("./localOnly-BHScJasw.js"));
3282
3282
  await handleLocalOnlyFlow(ctx, request, worker, {
3283
3283
  confirmationConfig,
3284
3284
  transactionSummary
3285
3285
  });
3286
3286
  },
3287
3287
  [SecureConfirmationType.REGISTER_ACCOUNT]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3288
- const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-DLPLsGCz.js"));
3288
+ const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-lDD60Ytt.js"));
3289
3289
  await handleRegistrationFlow(ctx, request, worker, {
3290
3290
  confirmationConfig,
3291
3291
  transactionSummary
3292
3292
  });
3293
3293
  },
3294
3294
  [SecureConfirmationType.LINK_DEVICE]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3295
- const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-DLPLsGCz.js"));
3295
+ const { handleRegistrationFlow } = await importFlow("registration", () => import("./registration-lDD60Ytt.js"));
3296
3296
  await handleRegistrationFlow(ctx, request, worker, {
3297
3297
  confirmationConfig,
3298
3298
  transactionSummary
3299
3299
  });
3300
3300
  },
3301
3301
  [SecureConfirmationType.SIGN_TRANSACTION]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3302
- const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-Bk-VavcV.js"));
3302
+ const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-BalIhtJ9.js"));
3303
3303
  await handleTransactionSigningFlow(ctx, request, worker, {
3304
3304
  confirmationConfig,
3305
3305
  transactionSummary
3306
3306
  });
3307
3307
  },
3308
3308
  [SecureConfirmationType.SIGN_NEP413_MESSAGE]: async ({ ctx, request, worker, confirmationConfig, transactionSummary }) => {
3309
- const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-Bk-VavcV.js"));
3309
+ const { handleTransactionSigningFlow } = await importFlow("transactions", () => import("./transactions-BalIhtJ9.js"));
3310
3310
  await handleTransactionSigningFlow(ctx, request, worker, {
3311
3311
  confirmationConfig,
3312
3312
  transactionSummary
@@ -6385,6 +6385,29 @@ var WebAuthnManager = class {
6385
6385
  async extractCosePublicKey(attestationObjectBase64url) {
6386
6386
  return await this.signerWorkerManager.extractCosePublicKey(attestationObjectBase64url);
6387
6387
  }
6388
+ /**
6389
+ * Helper to ensure the local `lastUser` pointer is consistent with the latest DB updates.
6390
+ * This fixes issues where a recovery or registration might have updated the user record
6391
+ * but failed to update the `lastUser` pointer (e.g. due to previous bugs or interruptions),
6392
+ * preventing the strict `ensureCurrentPasskey` check from passing.
6393
+ */
6394
+ async autoHealLastUserPointer(nearAccountId) {
6395
+ try {
6396
+ const lastUser = await this.getLastUser();
6397
+ if (lastUser && lastUser.nearAccountId === nearAccountId) {
6398
+ const freshest = await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId);
6399
+ if (freshest && freshest.deviceNumber !== lastUser.deviceNumber) {
6400
+ console.log(`[WebAuthnManager] Auto-healing lastUser pointer from device ${lastUser.deviceNumber} to ${freshest.deviceNumber}`);
6401
+ await this.setLastUser(nearAccountId, freshest.deviceNumber);
6402
+ }
6403
+ } else {
6404
+ const freshest = await IndexedDBManager.clientDB.getLastDBUpdatedUser(nearAccountId);
6405
+ if (freshest) await this.setLastUser(nearAccountId, freshest.deviceNumber);
6406
+ }
6407
+ } catch (e) {
6408
+ console.warn("[WebAuthnManager] Auto-heal pointer failed (non-fatal):", e);
6409
+ }
6410
+ }
6388
6411
  /** Worker-driven export: two-phase V2 (collect PRF → decrypt → show UI) */
6389
6412
  async exportNearKeypairWithUIWorkerDriven(nearAccountId, options) {
6390
6413
  await this.withSigningSession({
@@ -9845,8 +9868,30 @@ async function signDelegateAction(args) {
9845
9868
  //#endregion
9846
9869
  //#region src/core/TatchiPasskey/relay.ts
9847
9870
  init_sdkSentEvents();
9871
+ const toNumberArray = (value) => Array.isArray(value) ? value : Array.from(value);
9872
+ const normalizeSignedDelegateForRelay = (signedDelegate) => {
9873
+ const delegateAction = signedDelegate.delegateAction;
9874
+ const signature = signedDelegate.signature;
9875
+ return {
9876
+ delegateAction: {
9877
+ ...delegateAction,
9878
+ publicKey: {
9879
+ ...delegateAction.publicKey,
9880
+ keyData: toNumberArray(delegateAction.publicKey.keyData)
9881
+ }
9882
+ },
9883
+ signature: {
9884
+ ...signature,
9885
+ signatureData: toNumberArray(signature.signatureData)
9886
+ }
9887
+ };
9888
+ };
9848
9889
  async function sendDelegateActionViaRelayer(args) {
9849
9890
  const { url, payload, signal, options } = args;
9891
+ const normalizedPayload = {
9892
+ ...payload,
9893
+ signedDelegate: normalizeSignedDelegateForRelay(payload.signedDelegate)
9894
+ };
9850
9895
  const emit = (event) => options?.onEvent?.(event);
9851
9896
  const emitError = (message) => {
9852
9897
  emit({
@@ -9868,7 +9913,7 @@ async function sendDelegateActionViaRelayer(args) {
9868
9913
  res = await fetch(url, {
9869
9914
  method: "POST",
9870
9915
  headers: { "content-type": "application/json" },
9871
- body: JSON.stringify(payload),
9916
+ body: JSON.stringify(normalizedPayload),
9872
9917
  signal
9873
9918
  });
9874
9919
  } catch (err$1) {
@@ -9933,28 +9978,65 @@ const OFFLINE_EXPORT_FALLBACK = "OFFLINE_EXPORT_FALLBACK";
9933
9978
  const WALLET_UI_CLOSED = "WALLET_UI_CLOSED";
9934
9979
  const EXPORT_NEAR_KEYPAIR_CANCELLED = "EXPORT_NEAR_KEYPAIR_CANCELLED";
9935
9980
 
9981
+ //#endregion
9982
+ //#region src/core/EmailRecovery/emailRecoveryPendingStore.ts
9983
+ var EmailRecoveryPendingStore;
9984
+ var init_emailRecoveryPendingStore = __esm({ "src/core/EmailRecovery/emailRecoveryPendingStore.ts": (() => {
9985
+ init_IndexedDBManager();
9986
+ EmailRecoveryPendingStore = class {
9987
+ getPendingTtlMs;
9988
+ now;
9989
+ constructor(options) {
9990
+ this.getPendingTtlMs = options.getPendingTtlMs;
9991
+ this.now = options.now ?? Date.now;
9992
+ }
9993
+ getPendingIndexKey(accountId) {
9994
+ return `pendingEmailRecovery:${accountId}`;
9995
+ }
9996
+ getPendingRecordKey(accountId, nearPublicKey) {
9997
+ return `${this.getPendingIndexKey(accountId)}:${nearPublicKey}`;
9998
+ }
9999
+ async get(accountId, nearPublicKey) {
10000
+ const pendingTtlMs = this.getPendingTtlMs();
10001
+ const indexKey = this.getPendingIndexKey(accountId);
10002
+ const indexedNearPublicKey = await IndexedDBManager.clientDB.getAppState(indexKey);
10003
+ const resolvedNearPublicKey = nearPublicKey ?? indexedNearPublicKey;
10004
+ if (!resolvedNearPublicKey) return null;
10005
+ const recordKey = this.getPendingRecordKey(accountId, resolvedNearPublicKey);
10006
+ const record = await IndexedDBManager.clientDB.getAppState(recordKey);
10007
+ const shouldClearIndex = indexedNearPublicKey === resolvedNearPublicKey;
10008
+ if (!record) {
10009
+ if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10010
+ return null;
10011
+ }
10012
+ if (this.now() - record.createdAt > pendingTtlMs) {
10013
+ await IndexedDBManager.clientDB.setAppState(recordKey, void 0).catch(() => {});
10014
+ if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10015
+ return null;
10016
+ }
10017
+ await this.touchIndex(accountId, record.nearPublicKey);
10018
+ return record;
10019
+ }
10020
+ async set(record) {
10021
+ const key = this.getPendingRecordKey(record.accountId, record.nearPublicKey);
10022
+ await IndexedDBManager.clientDB.setAppState(key, record);
10023
+ await this.touchIndex(record.accountId, record.nearPublicKey);
10024
+ }
10025
+ async clear(accountId, nearPublicKey) {
10026
+ const indexKey = this.getPendingIndexKey(accountId);
10027
+ const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
10028
+ const resolvedNearPublicKey = nearPublicKey || idx || "";
10029
+ if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
10030
+ if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10031
+ }
10032
+ async touchIndex(accountId, nearPublicKey) {
10033
+ await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(accountId), nearPublicKey).catch(() => {});
10034
+ }
10035
+ };
10036
+ }) });
10037
+
9936
10038
  //#endregion
9937
10039
  //#region src/core/EmailRecovery/index.ts
9938
- init_accountIds();
9939
- init_IndexedDBManager();
9940
- const canonicalizeEmail = (email) => {
9941
- const raw = String(email || "").trim();
9942
- if (!raw) return "";
9943
- const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
9944
- const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
9945
- const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
9946
- const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
9947
- for (const candidate of candidates) {
9948
- const cleaned = candidate.replace(/^mailto:\s*/i, "");
9949
- const match = cleaned.match(emailRegex);
9950
- if (match?.[1]) return match[1].trim().toLowerCase();
9951
- }
9952
- return withoutHeaderName.toLowerCase();
9953
- };
9954
- const bytesToHex = (bytes) => {
9955
- const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
9956
- return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
9957
- };
9958
10040
  async function hashRecoveryEmails(emails, accountId) {
9959
10041
  const encoder = new TextEncoder();
9960
10042
  const salt = (accountId || "").trim().toLowerCase();
@@ -10001,6 +10083,30 @@ async function prepareRecoveryEmails(nearAccountId, recoveryEmails) {
10001
10083
  async function getLocalRecoveryEmails(nearAccountId) {
10002
10084
  return IndexedDBManager.getRecoveryEmails(nearAccountId);
10003
10085
  }
10086
+ var canonicalizeEmail, bytesToHex;
10087
+ var init_EmailRecovery = __esm({ "src/core/EmailRecovery/index.ts": (() => {
10088
+ init_accountIds();
10089
+ init_IndexedDBManager();
10090
+ init_emailRecoveryPendingStore();
10091
+ canonicalizeEmail = (email) => {
10092
+ const raw = String(email || "").trim();
10093
+ if (!raw) return "";
10094
+ const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
10095
+ const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
10096
+ const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
10097
+ const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
10098
+ for (const candidate of candidates) {
10099
+ const cleaned = candidate.replace(/^mailto:\s*/i, "");
10100
+ const match = cleaned.match(emailRegex);
10101
+ if (match?.[1]) return match[1].trim().toLowerCase();
10102
+ }
10103
+ return withoutHeaderName.toLowerCase();
10104
+ };
10105
+ bytesToHex = (bytes) => {
10106
+ const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
10107
+ return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
10108
+ };
10109
+ }) });
10004
10110
 
10005
10111
  //#endregion
10006
10112
  //#region src/core/TatchiPasskey/emailRecovery.ts
@@ -10047,9 +10153,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10047
10153
  init_rpc();
10048
10154
  init_getDeviceNumber();
10049
10155
  init_login();
10156
+ init_EmailRecovery();
10050
10157
  EmailRecoveryFlow = class {
10051
10158
  context;
10052
10159
  options;
10160
+ pendingStore;
10053
10161
  pending = null;
10054
10162
  phase = EmailRecoveryPhase.STEP_1_PREPARATION;
10055
10163
  pollingTimer;
@@ -10060,6 +10168,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10060
10168
  constructor(context, options) {
10061
10169
  this.context = context;
10062
10170
  this.options = options;
10171
+ this.pendingStore = options?.pendingStore ?? new EmailRecoveryPendingStore({ getPendingTtlMs: () => this.getConfig().pendingTtlMs });
10063
10172
  }
10064
10173
  setOptions(options) {
10065
10174
  if (!options) return;
@@ -10067,6 +10176,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10067
10176
  ...this.options || {},
10068
10177
  ...options
10069
10178
  };
10179
+ if (options.pendingStore) this.pendingStore = options.pendingStore;
10070
10180
  }
10071
10181
  emit(event) {
10072
10182
  this.options?.onEvent?.(event);
@@ -10085,24 +10195,139 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10085
10195
  this.options?.onError?.(err$1);
10086
10196
  return err$1;
10087
10197
  }
10198
+ async fail(step, message) {
10199
+ const err$1 = this.emitError(step, message);
10200
+ await this.options?.afterCall?.(false);
10201
+ throw err$1;
10202
+ }
10203
+ async assertValidAccountIdOrFail(step, accountId) {
10204
+ const validation = validateNearAccountId(accountId);
10205
+ if (!validation.valid) await this.fail(step, `Invalid NEAR account ID: ${validation.error}`);
10206
+ return toAccountId(accountId);
10207
+ }
10208
+ async resolvePendingOrFail(step, args, options) {
10209
+ const { allowErrorStatus = true, missingMessage = "No pending email recovery record found for this account", errorStatusMessage = "Pending email recovery is in an error state; please restart the flow" } = options ?? {};
10210
+ let rec = this.pending;
10211
+ if (!rec || rec.accountId !== args.accountId || args.nearPublicKey && rec.nearPublicKey !== args.nearPublicKey) {
10212
+ rec = await this.loadPending(args.accountId, args.nearPublicKey);
10213
+ this.pending = rec;
10214
+ }
10215
+ if (!rec) await this.fail(step, missingMessage);
10216
+ const resolved = rec;
10217
+ if (!allowErrorStatus && resolved.status === "error") await this.fail(step, errorStatusMessage);
10218
+ return resolved;
10219
+ }
10088
10220
  getConfig() {
10089
10221
  return getEmailRecoveryConfig(this.context.configs);
10090
10222
  }
10091
- getPendingIndexKey(accountId) {
10092
- return `pendingEmailRecovery:${accountId}`;
10223
+ toBigInt(value) {
10224
+ if (typeof value === "bigint") return value;
10225
+ if (typeof value === "number") return BigInt(value);
10226
+ if (typeof value === "string" && value.length > 0) return BigInt(value);
10227
+ return BigInt(0);
10093
10228
  }
10094
- getPendingRecordKey(accountId, nearPublicKey) {
10095
- return `${this.getPendingIndexKey(accountId)}:${nearPublicKey}`;
10229
+ computeAvailableBalance(accountView) {
10230
+ const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
10231
+ const amount = this.toBigInt(accountView.amount);
10232
+ const locked = this.toBigInt(accountView.locked);
10233
+ const storageUsage = this.toBigInt(accountView.storage_usage);
10234
+ const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
10235
+ const rawAvailable = amount - locked - storageCost;
10236
+ return rawAvailable > 0 ? rawAvailable : BigInt(0);
10237
+ }
10238
+ async assertSufficientBalance(nearAccountId) {
10239
+ const { minBalanceYocto } = this.getConfig();
10240
+ try {
10241
+ const accountView = await this.context.nearClient.viewAccount(nearAccountId);
10242
+ const available = this.computeAvailableBalance(accountView);
10243
+ if (available < BigInt(minBalanceYocto)) await this.fail(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
10244
+ } catch (e) {
10245
+ await this.fail(1, e?.message || "Failed to fetch account balance for recovery");
10246
+ }
10247
+ }
10248
+ async getCanonicalRecoveryEmailOrFail(recoveryEmail) {
10249
+ const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
10250
+ if (!canonicalEmail) await this.fail(1, "Recovery email is required for email-based account recovery");
10251
+ return canonicalEmail;
10252
+ }
10253
+ async getNextDeviceNumberFromContract(nearAccountId) {
10254
+ try {
10255
+ const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
10256
+ const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
10257
+ const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
10258
+ const max = numbers.length > 0 ? Math.max(...numbers) : 0;
10259
+ return max + 1;
10260
+ } catch {
10261
+ return 1;
10262
+ }
10096
10263
  }
10097
- async checkVerificationStatus(rec) {
10264
+ async collectRecoveryCredentialOrFail(nearAccountId, deviceNumber) {
10265
+ const confirmerText = {
10266
+ title: this.options?.confirmerText?.title ?? "Register New Recovery Account",
10267
+ body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
10268
+ };
10269
+ const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
10270
+ nearAccountId,
10271
+ deviceNumber,
10272
+ confirmerText,
10273
+ confirmationConfigOverride: this.options?.confirmationConfig
10274
+ });
10275
+ if (!confirm.confirmed || !confirm.credential) await this.fail(2, "User cancelled email recovery TouchID confirmation");
10276
+ return {
10277
+ credential: confirm.credential,
10278
+ vrfChallenge: confirm.vrfChallenge || void 0
10279
+ };
10280
+ }
10281
+ async deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, credential) {
10282
+ const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
10283
+ credential,
10284
+ nearAccountId
10285
+ });
10286
+ if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) await this.fail(2, "Failed to derive VRF keypair from PRF for email recovery");
10287
+ const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
10288
+ nearAccountId,
10289
+ credential,
10290
+ options: { deviceNumber }
10291
+ });
10292
+ if (!nearKeyResult.success || !nearKeyResult.publicKey) await this.fail(2, "Failed to derive NEAR keypair for email recovery");
10293
+ return {
10294
+ encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,
10295
+ serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,
10296
+ vrfPublicKey: vrfDerivationResult.vrfPublicKey,
10297
+ nearPublicKey: nearKeyResult.publicKey
10298
+ };
10299
+ }
10300
+ emitAwaitEmail(rec, mailtoUrl) {
10301
+ this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;
10302
+ this.emit({
10303
+ step: 3,
10304
+ phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
10305
+ status: EmailRecoveryStatus.PROGRESS,
10306
+ message: "New device key created; please send the recovery email from your registered address.",
10307
+ data: {
10308
+ accountId: rec.accountId,
10309
+ recoveryEmail: rec.recoveryEmail,
10310
+ nearPublicKey: rec.nearPublicKey,
10311
+ requestId: rec.requestId,
10312
+ mailtoUrl
10313
+ }
10314
+ });
10315
+ }
10316
+ emitAutoLoginEvent(status, message, data) {
10317
+ this.emit({
10318
+ step: 5,
10319
+ phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10320
+ status,
10321
+ message,
10322
+ data
10323
+ });
10324
+ }
10325
+ async checkViaDkimViewMethod(rec) {
10098
10326
  const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();
10099
10327
  if (!dkimVerifierAccountId) return null;
10100
10328
  try {
10101
- const result = await this.context.nearClient.view({
10102
- account: dkimVerifierAccountId,
10103
- method: verificationViewMethod,
10104
- args: { request_id: rec.requestId }
10105
- });
10329
+ const { getEmailRecoveryVerificationResult } = await import("./rpcCalls-BQrJMTdg.js");
10330
+ const result = await getEmailRecoveryVerificationResult(this.context.nearClient, dkimVerifierAccountId, verificationViewMethod, rec.requestId);
10106
10331
  if (!result) return {
10107
10332
  completed: false,
10108
10333
  success: false
@@ -10134,43 +10359,78 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10134
10359
  transactionHash: result.transaction_hash
10135
10360
  };
10136
10361
  } catch (err$1) {
10137
- console.warn("[EmailRecoveryFlow] get_verification_result view failed; falling back to access key polling", err$1);
10362
+ console.warn("[EmailRecoveryFlow] get_verification_result view failed; will retry", err$1);
10138
10363
  return null;
10139
10364
  }
10140
10365
  }
10141
- async loadPending(accountId, nearPublicKey) {
10142
- const { pendingTtlMs } = this.getConfig();
10143
- const indexKey = this.getPendingIndexKey(accountId);
10144
- const indexedNearPublicKey = await IndexedDBManager.clientDB.getAppState(indexKey);
10145
- const resolvedNearPublicKey = nearPublicKey ?? indexedNearPublicKey;
10146
- if (!resolvedNearPublicKey) return null;
10147
- const recordKey = this.getPendingRecordKey(accountId, resolvedNearPublicKey);
10148
- const record = await IndexedDBManager.clientDB.getAppState(recordKey);
10149
- const shouldClearIndex = indexedNearPublicKey === resolvedNearPublicKey;
10150
- if (!record) {
10151
- if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10152
- return null;
10153
- }
10154
- if (Date.now() - record.createdAt > pendingTtlMs) {
10155
- await IndexedDBManager.clientDB.setAppState(recordKey, void 0).catch(() => {});
10156
- if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10157
- return null;
10366
+ buildPollingEventData(rec, details) {
10367
+ return {
10368
+ accountId: rec.accountId,
10369
+ requestId: rec.requestId,
10370
+ nearPublicKey: rec.nearPublicKey,
10371
+ transactionHash: details.transactionHash,
10372
+ elapsedMs: details.elapsedMs,
10373
+ pollCount: details.pollCount
10374
+ };
10375
+ }
10376
+ async sleepForPollInterval(ms) {
10377
+ await new Promise((resolve) => {
10378
+ this.pollIntervalResolver = resolve;
10379
+ this.pollingTimer = setTimeout(() => {
10380
+ this.pollIntervalResolver = void 0;
10381
+ this.pollingTimer = void 0;
10382
+ resolve();
10383
+ }, ms);
10384
+ }).finally(() => {
10385
+ this.pollIntervalResolver = void 0;
10386
+ });
10387
+ }
10388
+ async pollUntil(args) {
10389
+ const now = args.now ?? Date.now;
10390
+ const sleep = args.sleep ?? this.sleepForPollInterval.bind(this);
10391
+ const startedAt = now();
10392
+ let pollCount = 0;
10393
+ while (!args.isCancelled()) {
10394
+ pollCount += 1;
10395
+ const elapsedMs$1 = now() - startedAt;
10396
+ if (elapsedMs$1 > args.timeoutMs) return {
10397
+ status: "timedOut",
10398
+ elapsedMs: elapsedMs$1,
10399
+ pollCount
10400
+ };
10401
+ const result = await args.tick({
10402
+ elapsedMs: elapsedMs$1,
10403
+ pollCount
10404
+ });
10405
+ if (result.done) return {
10406
+ status: "completed",
10407
+ value: result.value,
10408
+ elapsedMs: elapsedMs$1,
10409
+ pollCount
10410
+ };
10411
+ if (args.isCancelled()) return {
10412
+ status: "cancelled",
10413
+ elapsedMs: elapsedMs$1,
10414
+ pollCount
10415
+ };
10416
+ await sleep(args.intervalMs);
10158
10417
  }
10159
- await IndexedDBManager.clientDB.setAppState(indexKey, record.nearPublicKey).catch(() => {});
10160
- return record;
10418
+ const elapsedMs = now() - startedAt;
10419
+ return {
10420
+ status: "cancelled",
10421
+ elapsedMs,
10422
+ pollCount
10423
+ };
10424
+ }
10425
+ async loadPending(accountId, nearPublicKey) {
10426
+ return this.pendingStore.get(accountId, nearPublicKey);
10161
10427
  }
10162
10428
  async savePending(rec) {
10163
- const key = this.getPendingRecordKey(rec.accountId, rec.nearPublicKey);
10164
- await IndexedDBManager.clientDB.setAppState(key, rec);
10165
- await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(rec.accountId), rec.nearPublicKey).catch(() => {});
10429
+ await this.pendingStore.set(rec);
10166
10430
  this.pending = rec;
10167
10431
  }
10168
10432
  async clearPending(accountId, nearPublicKey) {
10169
- const indexKey = this.getPendingIndexKey(accountId);
10170
- const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
10171
- const resolvedNearPublicKey = nearPublicKey || idx || "";
10172
- if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
10173
- if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
10433
+ await this.pendingStore.clear(accountId, nearPublicKey);
10174
10434
  if (this.pending && this.pending.accountId === accountId && (!nearPublicKey || this.pending.nearPublicKey === nearPublicKey)) this.pending = null;
10175
10435
  }
10176
10436
  getState() {
@@ -10184,48 +10444,14 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10184
10444
  const { accountId, nearPublicKey } = args;
10185
10445
  this.cancelled = false;
10186
10446
  this.error = void 0;
10187
- const validation = validateNearAccountId(accountId);
10188
- if (!validation.valid) {
10189
- const err$1 = this.emitError(3, `Invalid NEAR account ID: ${validation.error}`);
10190
- await this.options?.afterCall?.(false);
10191
- throw err$1;
10192
- }
10193
- const nearAccountId = toAccountId(accountId);
10194
- let rec = this.pending;
10195
- if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
10196
- rec = await this.loadPending(nearAccountId, nearPublicKey);
10197
- this.pending = rec;
10198
- }
10199
- if (!rec) {
10200
- const err$1 = this.emitError(3, "No pending email recovery record found for this account");
10201
- await this.options?.afterCall?.(false);
10202
- throw err$1;
10203
- }
10204
- if (rec.status === "error") {
10205
- const err$1 = this.emitError(3, "Pending email recovery is in an error state; please restart the flow");
10206
- await this.options?.afterCall?.(false);
10207
- throw err$1;
10208
- }
10209
- if (rec.status === "finalizing" || rec.status === "complete") {
10210
- const err$1 = this.emitError(3, "Recovery email has already been processed on-chain for this request");
10211
- await this.options?.afterCall?.(false);
10212
- throw err$1;
10213
- }
10447
+ const nearAccountId = await this.assertValidAccountIdOrFail(3, accountId);
10448
+ const rec = await this.resolvePendingOrFail(3, {
10449
+ accountId: nearAccountId,
10450
+ nearPublicKey
10451
+ }, { allowErrorStatus: false });
10452
+ if (rec.status === "finalizing" || rec.status === "complete") await this.fail(3, "Recovery email has already been processed on-chain for this request");
10214
10453
  const mailtoUrl = rec.status === "awaiting-email" ? await this.buildMailtoUrlAndUpdateStatus(rec) : this.buildMailtoUrlInternal(rec);
10215
- this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;
10216
- this.emit({
10217
- step: 3,
10218
- phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
10219
- status: EmailRecoveryStatus.PROGRESS,
10220
- message: "New device key created; please send the recovery email from your registered address.",
10221
- data: {
10222
- accountId: rec.accountId,
10223
- recoveryEmail: rec.recoveryEmail,
10224
- nearPublicKey: rec.nearPublicKey,
10225
- requestId: rec.requestId,
10226
- mailtoUrl
10227
- }
10228
- });
10454
+ this.emitAwaitEmail(rec, mailtoUrl);
10229
10455
  await this.options?.afterCall?.(true, void 0);
10230
10456
  return mailtoUrl;
10231
10457
  }
@@ -10240,49 +10466,10 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10240
10466
  status: EmailRecoveryStatus.PROGRESS,
10241
10467
  message: "Preparing email recovery..."
10242
10468
  });
10243
- const validation = validateNearAccountId(accountId);
10244
- if (!validation.valid) {
10245
- const err$1 = this.emitError(1, `Invalid NEAR account ID: ${validation.error}`);
10246
- await this.options?.afterCall?.(false);
10247
- throw err$1;
10248
- }
10249
- const nearAccountId = toAccountId(accountId);
10250
- const { minBalanceYocto } = this.getConfig();
10251
- const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
10252
- try {
10253
- const accountView = await this.context.nearClient.viewAccount(nearAccountId);
10254
- const amount = BigInt(accountView.amount || "0");
10255
- const locked = BigInt(accountView.locked || "0");
10256
- const storageUsage = BigInt(accountView.storage_usage || 0);
10257
- const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
10258
- const rawAvailable = amount - locked - storageCost;
10259
- const available = rawAvailable > 0 ? rawAvailable : BigInt(0);
10260
- if (available < BigInt(minBalanceYocto)) {
10261
- const err$1 = this.emitError(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
10262
- await this.options?.afterCall?.(false);
10263
- throw err$1;
10264
- }
10265
- } catch (e) {
10266
- const err$1 = this.emitError(1, e?.message || "Failed to fetch account balance for recovery");
10267
- await this.options?.afterCall?.(false);
10268
- throw err$1;
10269
- }
10270
- const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
10271
- if (!canonicalEmail) {
10272
- const err$1 = this.emitError(1, "Recovery email is required for email-based account recovery");
10273
- await this.options?.afterCall?.(false);
10274
- throw err$1;
10275
- }
10276
- let deviceNumber = 1;
10277
- try {
10278
- const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
10279
- const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
10280
- const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
10281
- const max = numbers.length > 0 ? Math.max(...numbers) : 0;
10282
- deviceNumber = max + 1;
10283
- } catch {
10284
- deviceNumber = 1;
10285
- }
10469
+ const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);
10470
+ await this.assertSufficientBalance(nearAccountId);
10471
+ const canonicalEmail = await this.getCanonicalRecoveryEmailOrFail(recoveryEmail);
10472
+ const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);
10286
10473
  this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;
10287
10474
  this.emit({
10288
10475
  step: 2,
@@ -10291,69 +10478,24 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10291
10478
  message: "Collecting passkey for email recovery..."
10292
10479
  });
10293
10480
  try {
10294
- const confirmerText = {
10295
- title: this.options?.confirmerText?.title ?? "Register New Recovery Account",
10296
- body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
10297
- };
10298
- const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
10299
- nearAccountId,
10300
- deviceNumber,
10301
- confirmerText,
10302
- confirmationConfigOverride: this.options?.confirmationConfig
10303
- });
10304
- if (!confirm.confirmed || !confirm.credential) {
10305
- const err$1 = this.emitError(2, "User cancelled email recovery TouchID confirmation");
10306
- await this.options?.afterCall?.(false);
10307
- throw err$1;
10308
- }
10309
- const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
10310
- credential: confirm.credential,
10311
- nearAccountId
10312
- });
10313
- if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) {
10314
- const err$1 = this.emitError(2, "Failed to derive VRF keypair from PRF for email recovery");
10315
- await this.options?.afterCall?.(false);
10316
- throw err$1;
10317
- }
10318
- const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
10319
- nearAccountId,
10320
- credential: confirm.credential,
10321
- options: { deviceNumber }
10322
- });
10323
- if (!nearKeyResult.success || !nearKeyResult.publicKey) {
10324
- const err$1 = this.emitError(2, "Failed to derive NEAR keypair for email recovery");
10325
- await this.options?.afterCall?.(false);
10326
- throw err$1;
10327
- }
10481
+ const confirm = await this.collectRecoveryCredentialOrFail(nearAccountId, deviceNumber);
10482
+ const derivedKeys = await this.deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, confirm.credential);
10328
10483
  const rec = {
10329
10484
  accountId: nearAccountId,
10330
10485
  recoveryEmail: canonicalEmail,
10331
10486
  deviceNumber,
10332
- nearPublicKey: nearKeyResult.publicKey,
10487
+ nearPublicKey: derivedKeys.nearPublicKey,
10333
10488
  requestId: generateEmailRecoveryRequestId(),
10334
- encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,
10335
- serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,
10336
- vrfPublicKey: vrfDerivationResult.vrfPublicKey,
10489
+ encryptedVrfKeypair: derivedKeys.encryptedVrfKeypair,
10490
+ serverEncryptedVrfKeypair: derivedKeys.serverEncryptedVrfKeypair,
10491
+ vrfPublicKey: derivedKeys.vrfPublicKey,
10337
10492
  credential: confirm.credential,
10338
10493
  vrfChallenge: confirm.vrfChallenge || void 0,
10339
10494
  createdAt: Date.now(),
10340
10495
  status: "awaiting-email"
10341
10496
  };
10342
10497
  const mailtoUrl = await this.buildMailtoUrlAndUpdateStatus(rec);
10343
- this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;
10344
- this.emit({
10345
- step: 3,
10346
- phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
10347
- status: EmailRecoveryStatus.PROGRESS,
10348
- message: "New device key created; please send the recovery email from your registered address.",
10349
- data: {
10350
- accountId: rec.accountId,
10351
- recoveryEmail: rec.recoveryEmail,
10352
- nearPublicKey: rec.nearPublicKey,
10353
- requestId: rec.requestId,
10354
- mailtoUrl
10355
- }
10356
- });
10498
+ this.emitAwaitEmail(rec, mailtoUrl);
10357
10499
  await this.options?.afterCall?.(true, void 0);
10358
10500
  return {
10359
10501
  mailtoUrl,
@@ -10381,28 +10523,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10381
10523
  const { accountId, nearPublicKey } = args;
10382
10524
  this.cancelled = false;
10383
10525
  this.error = void 0;
10384
- const validation = validateNearAccountId(accountId);
10385
- if (!validation.valid) {
10386
- const err$1 = this.emitError(4, `Invalid NEAR account ID: ${validation.error}`);
10387
- await this.options?.afterCall?.(false);
10388
- throw err$1;
10389
- }
10390
- const nearAccountId = toAccountId(accountId);
10391
- let rec = this.pending;
10392
- if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
10393
- rec = await this.loadPending(nearAccountId, nearPublicKey);
10394
- this.pending = rec;
10395
- }
10396
- if (!rec) {
10397
- const err$1 = this.emitError(4, "No pending email recovery record found for this account");
10398
- await this.options?.afterCall?.(false);
10399
- throw err$1;
10400
- }
10401
- if (rec.status === "error") {
10402
- const err$1 = this.emitError(4, "Pending email recovery is in an error state; please restart the flow");
10403
- await this.options?.afterCall?.(false);
10404
- throw err$1;
10405
- }
10526
+ const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
10527
+ const rec = await this.resolvePendingOrFail(4, {
10528
+ accountId: nearAccountId,
10529
+ nearPublicKey
10530
+ }, { allowErrorStatus: false });
10406
10531
  if (rec.status === "complete" || rec.status === "finalizing") {
10407
10532
  await this.options?.afterCall?.(true, void 0);
10408
10533
  return;
@@ -10442,23 +10567,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10442
10567
  const { accountId, nearPublicKey } = args;
10443
10568
  this.cancelled = false;
10444
10569
  this.error = void 0;
10445
- const validation = validateNearAccountId(accountId);
10446
- if (!validation.valid) {
10447
- const err$1 = this.emitError(4, `Invalid NEAR account ID: ${validation.error}`);
10448
- await this.options?.afterCall?.(false);
10449
- throw err$1;
10450
- }
10451
- const nearAccountId = toAccountId(accountId);
10452
- let rec = this.pending;
10453
- if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
10454
- rec = await this.loadPending(nearAccountId, nearPublicKey);
10455
- this.pending = rec;
10456
- }
10457
- if (!rec) {
10458
- const err$1 = this.emitError(4, "No pending email recovery record found for this account");
10459
- await this.options?.afterCall?.(false);
10460
- throw err$1;
10461
- }
10570
+ const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
10571
+ const rec = await this.resolvePendingOrFail(4, {
10572
+ accountId: nearAccountId,
10573
+ nearPublicKey
10574
+ }, { allowErrorStatus: true });
10462
10575
  this.emit({
10463
10576
  step: 0,
10464
10577
  phase: EmailRecoveryPhase.RESUMED_FROM_PENDING,
@@ -10494,245 +10607,230 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10494
10607
  }
10495
10608
  this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;
10496
10609
  this.pollingStartedAt = Date.now();
10497
- let pollCount = 0;
10498
- while (!this.cancelled) {
10499
- pollCount += 1;
10500
- const elapsed = Date.now() - (this.pollingStartedAt || 0);
10501
- if (elapsed > maxPollingDurationMs) {
10502
- const err$2 = this.emitError(4, "Timed out waiting for recovery email to be processed on-chain");
10610
+ const pollResult = await this.pollUntil({
10611
+ intervalMs: pollingIntervalMs,
10612
+ timeoutMs: maxPollingDurationMs,
10613
+ isCancelled: () => this.cancelled,
10614
+ tick: async ({ elapsedMs, pollCount }) => {
10615
+ const verification = await this.checkViaDkimViewMethod(rec);
10616
+ const completed = verification?.completed === true;
10617
+ const success = verification?.success === true;
10618
+ this.emit({
10619
+ step: 4,
10620
+ phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
10621
+ status: EmailRecoveryStatus.PROGRESS,
10622
+ message: completed && success ? `Email verified for request ${rec.requestId}; finalizing registration` : `Waiting for email verification for request ${rec.requestId}`,
10623
+ data: this.buildPollingEventData(rec, {
10624
+ transactionHash: verification?.transactionHash,
10625
+ elapsedMs,
10626
+ pollCount
10627
+ })
10628
+ });
10629
+ if (!completed) return { done: false };
10630
+ if (!success) return {
10631
+ done: true,
10632
+ value: {
10633
+ outcome: "failed",
10634
+ errorMessage: verification?.errorMessage || "Email verification failed"
10635
+ }
10636
+ };
10637
+ return {
10638
+ done: true,
10639
+ value: { outcome: "verified" }
10640
+ };
10641
+ }
10642
+ });
10643
+ if (pollResult.status === "completed") {
10644
+ if (pollResult.value.outcome === "failed") {
10645
+ const err$2 = this.emitError(4, pollResult.value.errorMessage);
10503
10646
  rec.status = "error";
10504
10647
  await this.savePending(rec);
10505
10648
  await this.options?.afterCall?.(false);
10506
10649
  throw err$2;
10507
10650
  }
10508
- const verification = await this.checkVerificationStatus(rec);
10509
- const completed = verification?.completed === true;
10510
- const success = verification?.success === true;
10511
- this.emit({
10512
- step: 4,
10513
- phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
10514
- status: EmailRecoveryStatus.PROGRESS,
10515
- message: completed && success ? `Email verified for request ${rec.requestId}; finalizing registration` : `Waiting for email verification for request ${rec.requestId}`,
10516
- data: {
10517
- accountId: rec.accountId,
10518
- requestId: rec.requestId,
10519
- nearPublicKey: rec.nearPublicKey,
10520
- transactionHash: verification?.transactionHash,
10521
- elapsedMs: elapsed,
10522
- pollCount
10523
- }
10524
- });
10525
- if (completed) {
10526
- if (!success) {
10527
- const err$2 = this.emitError(4, verification?.errorMessage || "Email verification failed");
10528
- rec.status = "error";
10529
- await this.savePending(rec);
10530
- await this.options?.afterCall?.(false);
10531
- throw err$2;
10532
- }
10533
- rec.status = "finalizing";
10534
- await this.savePending(rec);
10535
- return;
10536
- }
10537
- if (this.cancelled) break;
10538
- await new Promise((resolve) => {
10539
- this.pollIntervalResolver = resolve;
10540
- this.pollingTimer = setTimeout(() => {
10541
- this.pollIntervalResolver = void 0;
10542
- this.pollingTimer = void 0;
10543
- resolve();
10544
- }, pollingIntervalMs);
10545
- }).finally(() => {
10546
- this.pollIntervalResolver = void 0;
10547
- });
10651
+ rec.status = "finalizing";
10652
+ await this.savePending(rec);
10653
+ return;
10654
+ }
10655
+ if (pollResult.status === "timedOut") {
10656
+ const err$2 = this.emitError(4, "Timed out waiting for recovery email to be processed on-chain");
10657
+ rec.status = "error";
10658
+ await this.savePending(rec);
10659
+ await this.options?.afterCall?.(false);
10660
+ throw err$2;
10548
10661
  }
10549
10662
  const err$1 = this.emitError(4, "Email recovery polling was cancelled");
10550
10663
  await this.options?.afterCall?.(false);
10551
10664
  throw err$1;
10552
10665
  }
10553
- async finalizeRegistration(rec) {
10554
- this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
10555
- this.emit({
10556
- step: 5,
10557
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10558
- status: EmailRecoveryStatus.PROGRESS,
10559
- message: "Finalizing email recovery registration...",
10560
- data: {
10561
- accountId: rec.accountId,
10562
- nearPublicKey: rec.nearPublicKey
10563
- }
10564
- });
10666
+ initializeNonceManager(rec) {
10565
10667
  const nonceManager = this.context.webAuthnManager.getNonceManager();
10566
10668
  const accountId = toAccountId(rec.accountId);
10567
10669
  nonceManager.initializeUser(accountId, rec.nearPublicKey);
10670
+ return {
10671
+ nonceManager,
10672
+ accountId
10673
+ };
10674
+ }
10675
+ async signRegistrationTx(rec, accountId) {
10676
+ const vrfChallenge = rec.vrfChallenge;
10677
+ if (!vrfChallenge) return this.fail(5, "Missing VRF challenge for email recovery registration");
10678
+ const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
10679
+ nearAccountId: accountId,
10680
+ credential: rec.credential,
10681
+ vrfChallenge,
10682
+ deterministicVrfPublicKey: rec.vrfPublicKey,
10683
+ deviceNumber: rec.deviceNumber
10684
+ });
10685
+ if (!registrationResult.success || !registrationResult.signedTransaction) await this.fail(5, registrationResult.error || "Failed to sign email recovery registration transaction");
10686
+ return registrationResult.signedTransaction;
10687
+ }
10688
+ async broadcastRegistrationTxAndWaitFinal(rec, signedTx) {
10568
10689
  try {
10569
- if (!rec.vrfChallenge) {
10570
- const err$1 = this.emitError(5, "Missing VRF challenge for email recovery registration");
10571
- await this.options?.afterCall?.(false);
10572
- throw err$1;
10573
- }
10574
- const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
10575
- nearAccountId: accountId,
10576
- credential: rec.credential,
10577
- vrfChallenge: rec.vrfChallenge,
10578
- deterministicVrfPublicKey: rec.vrfPublicKey,
10579
- deviceNumber: rec.deviceNumber
10580
- });
10581
- if (!registrationResult.success || !registrationResult.signedTransaction) {
10582
- const err$1 = this.emitError(5, registrationResult.error || "Failed to sign email recovery registration transaction");
10583
- await this.options?.afterCall?.(false);
10584
- throw err$1;
10585
- }
10586
- const signedTx = registrationResult.signedTransaction;
10690
+ const txResult = await this.context.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.linkDeviceRegistration);
10587
10691
  try {
10588
- const txResult = await this.context.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.linkDeviceRegistration);
10589
- try {
10590
- const txHash = txResult?.transaction?.hash || txResult?.transaction_hash;
10591
- if (txHash) {
10592
- this.emit({
10593
- step: 5,
10594
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10595
- status: EmailRecoveryStatus.PROGRESS,
10596
- message: "Registration transaction confirmed",
10597
- data: {
10598
- accountId: rec.accountId,
10599
- nearPublicKey: rec.nearPublicKey,
10600
- transactionHash: txHash
10601
- }
10602
- });
10603
- try {
10604
- await IndexedDBManager.clientDB.storeWebAuthnUserData({
10605
- nearAccountId: accountId,
10606
- deviceNumber: rec.deviceNumber,
10607
- clientNearPublicKey: rec.nearPublicKey,
10608
- passkeyCredential: {
10609
- id: rec.credential.id,
10610
- rawId: rec.credential.rawId
10611
- },
10612
- encryptedVrfKeypair: rec.encryptedVrfKeypair,
10613
- serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
10614
- });
10615
- const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
10616
- const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
10617
- const mappedAuthenticators = authenticators.map(({ authenticator }) => ({
10618
- credentialId: authenticator.credentialId,
10619
- credentialPublicKey: authenticator.credentialPublicKey,
10620
- transports: authenticator.transports,
10621
- name: authenticator.name,
10622
- registered: authenticator.registered.toISOString(),
10623
- vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
10624
- deviceNumber: authenticator.deviceNumber
10625
- }));
10626
- await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
10627
- await IndexedDBManager.clientDB.setLastUser(accountId, rec.deviceNumber);
10628
- } catch (syncErr) {
10629
- console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", syncErr);
10630
- }
10692
+ const txHash = txResult?.transaction?.hash || txResult?.transaction_hash;
10693
+ if (txHash) this.emit({
10694
+ step: 5,
10695
+ phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10696
+ status: EmailRecoveryStatus.PROGRESS,
10697
+ message: "Registration transaction confirmed",
10698
+ data: {
10699
+ accountId: rec.accountId,
10700
+ nearPublicKey: rec.nearPublicKey,
10701
+ transactionHash: txHash
10631
10702
  }
10632
- } catch {}
10633
- } catch (e) {
10634
- const msg = String(e?.message || "");
10635
- const err$1 = this.emitError(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
10636
- await this.options?.afterCall?.(false);
10637
- throw err$1;
10638
- }
10639
- try {
10640
- const txNonce = signedTx.transaction?.nonce;
10641
- if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
10703
+ });
10704
+ return txHash;
10642
10705
  } catch {}
10706
+ } catch (e) {
10707
+ const msg = String(e?.message || "");
10708
+ await this.fail(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
10709
+ }
10710
+ return void 0;
10711
+ }
10712
+ mapAuthenticatorsFromContract(authenticators) {
10713
+ return authenticators.map(({ authenticator }) => ({
10714
+ credentialId: authenticator.credentialId,
10715
+ credentialPublicKey: authenticator.credentialPublicKey,
10716
+ transports: authenticator.transports,
10717
+ name: authenticator.name,
10718
+ registered: authenticator.registered.toISOString(),
10719
+ vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
10720
+ deviceNumber: authenticator.deviceNumber
10721
+ }));
10722
+ }
10723
+ async syncAuthenticatorsBestEffort(accountId) {
10724
+ try {
10725
+ const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
10726
+ const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
10727
+ const mappedAuthenticators = this.mapAuthenticatorsFromContract(authenticators);
10728
+ await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
10729
+ return true;
10730
+ } catch (err$1) {
10731
+ console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", err$1);
10732
+ return false;
10733
+ }
10734
+ }
10735
+ async setLastUserBestEffort(accountId, deviceNumber) {
10736
+ try {
10737
+ await IndexedDBManager.clientDB.setLastUser(accountId, deviceNumber);
10738
+ return true;
10739
+ } catch (err$1) {
10740
+ console.warn("[EmailRecoveryFlow] Failed to set last user after recovery:", err$1);
10741
+ return false;
10742
+ }
10743
+ }
10744
+ async updateNonceBestEffort(nonceManager, signedTx) {
10745
+ try {
10746
+ const txNonce = signedTx.transaction?.nonce;
10747
+ if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
10748
+ } catch {}
10749
+ }
10750
+ async persistRecoveredUserData(rec, accountId) {
10751
+ const { webAuthnManager } = this.context;
10752
+ const payload = {
10753
+ nearAccountId: accountId,
10754
+ deviceNumber: rec.deviceNumber,
10755
+ clientNearPublicKey: rec.nearPublicKey,
10756
+ lastUpdated: Date.now(),
10757
+ passkeyCredential: {
10758
+ id: rec.credential.id,
10759
+ rawId: rec.credential.rawId
10760
+ },
10761
+ encryptedVrfKeypair: {
10762
+ encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
10763
+ chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
10764
+ },
10765
+ serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
10766
+ };
10767
+ await webAuthnManager.storeUserData(payload);
10768
+ }
10769
+ /**
10770
+ * Explicitly persist the authenticator from the recovery record into the local cache.
10771
+ * This ensures the key is available immediately, bridging the gap before RPC sync sees it.
10772
+ */
10773
+ async persistAuthenticatorBestEffort(rec, accountId) {
10774
+ try {
10643
10775
  const { webAuthnManager } = this.context;
10644
- await webAuthnManager.storeUserData({
10776
+ const attestationB64u = rec.credential.response.attestationObject;
10777
+ const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);
10778
+ await webAuthnManager.storeAuthenticator({
10645
10779
  nearAccountId: accountId,
10646
10780
  deviceNumber: rec.deviceNumber,
10647
- clientNearPublicKey: rec.nearPublicKey,
10648
- lastUpdated: Date.now(),
10649
- passkeyCredential: {
10650
- id: rec.credential.id,
10651
- rawId: rec.credential.rawId
10652
- },
10653
- encryptedVrfKeypair: {
10654
- encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
10655
- chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
10656
- },
10657
- serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
10658
- });
10659
- try {
10660
- const attestationB64u = rec.credential.response.attestationObject;
10661
- const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);
10662
- await webAuthnManager.storeAuthenticator({
10663
- nearAccountId: accountId,
10664
- deviceNumber: rec.deviceNumber,
10665
- credentialId: rec.credential.rawId,
10666
- credentialPublicKey,
10667
- transports: ["internal"],
10668
- name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
10669
- registered: (/* @__PURE__ */ new Date()).toISOString(),
10670
- syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
10671
- vrfPublicKey: rec.vrfPublicKey
10672
- });
10673
- } catch {}
10674
- await this.attemptAutoLogin(rec);
10675
- rec.status = "complete";
10676
- await this.savePending(rec);
10677
- await this.clearPending(rec.accountId, rec.nearPublicKey);
10678
- this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
10679
- this.emit({
10680
- step: 6,
10681
- phase: EmailRecoveryPhase.STEP_6_COMPLETE,
10682
- status: EmailRecoveryStatus.SUCCESS,
10683
- message: "Email recovery completed successfully",
10684
- data: {
10685
- accountId: rec.accountId,
10686
- nearPublicKey: rec.nearPublicKey
10687
- }
10781
+ credentialId: rec.credential.rawId,
10782
+ credentialPublicKey,
10783
+ transports: ["internal"],
10784
+ name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
10785
+ registered: (/* @__PURE__ */ new Date()).toISOString(),
10786
+ syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
10787
+ vrfPublicKey: rec.vrfPublicKey
10688
10788
  });
10789
+ console.log("[EmailRecoveryFlow] Locally persisted recovered authenticator for immediate use.");
10689
10790
  } catch (e) {
10690
- const err$1 = this.emitError(5, e?.message || "Email recovery finalization failed");
10691
- await this.options?.afterCall?.(false);
10692
- throw err$1;
10791
+ console.error("[EmailRecoveryFlow] Failed to locally persist authenticator (critical for immediate export):", e);
10693
10792
  }
10694
10793
  }
10695
- async attemptAutoLogin(rec) {
10794
+ async markCompleteAndClearPending(rec) {
10795
+ rec.status = "complete";
10796
+ await this.savePending(rec);
10797
+ await this.clearPending(rec.accountId, rec.nearPublicKey);
10798
+ }
10799
+ async assertVrfActiveForAccount(accountId, message) {
10800
+ const vrfStatus = await this.context.webAuthnManager.checkVrfStatus();
10801
+ const vrfActiveForAccount = vrfStatus.active && vrfStatus.nearAccountId && String(vrfStatus.nearAccountId) === String(accountId);
10802
+ if (!vrfActiveForAccount) throw new Error(message);
10803
+ }
10804
+ async finalizeLocalLoginState(accountId, deviceNumber) {
10805
+ const { webAuthnManager } = this.context;
10806
+ await webAuthnManager.setLastUser(accountId, deviceNumber);
10807
+ await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
10696
10808
  try {
10697
- this.emit({
10698
- step: 5,
10699
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10700
- status: EmailRecoveryStatus.PROGRESS,
10701
- message: "Attempting auto-login with recovered device...",
10702
- data: { autoLogin: "progress" }
10809
+ await getLoginSession(this.context, accountId);
10810
+ } catch {}
10811
+ }
10812
+ async tryShamirUnlock(rec, accountId, deviceNumber) {
10813
+ if (!rec.serverEncryptedVrfKeypair || !rec.serverEncryptedVrfKeypair.serverKeyId || !this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) return false;
10814
+ try {
10815
+ const { webAuthnManager } = this.context;
10816
+ const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
10817
+ nearAccountId: accountId,
10818
+ kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
10819
+ ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
10820
+ serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
10703
10821
  });
10822
+ if (!unlockResult.success) return false;
10823
+ await this.assertVrfActiveForAccount(accountId, "VRF session inactive after Shamir3Pass unlock");
10824
+ await this.finalizeLocalLoginState(accountId, deviceNumber);
10825
+ return true;
10826
+ } catch (err$1) {
10827
+ console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
10828
+ return false;
10829
+ }
10830
+ }
10831
+ async tryTouchIdUnlock(rec, accountId, deviceNumber) {
10832
+ try {
10704
10833
  const { webAuthnManager } = this.context;
10705
- const accountId = toAccountId(rec.accountId);
10706
- const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
10707
- if (deviceNumber === null) throw new Error(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
10708
- if (rec.serverEncryptedVrfKeypair && rec.serverEncryptedVrfKeypair.serverKeyId && this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) try {
10709
- const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
10710
- nearAccountId: accountId,
10711
- kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
10712
- ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
10713
- serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
10714
- });
10715
- if (unlockResult.success) {
10716
- const vrfStatus$1 = await webAuthnManager.checkVrfStatus();
10717
- const vrfActiveForAccount$1 = vrfStatus$1.active && vrfStatus$1.nearAccountId && String(vrfStatus$1.nearAccountId) === String(accountId);
10718
- if (!vrfActiveForAccount$1) throw new Error("VRF session inactive after Shamir3Pass unlock");
10719
- await webAuthnManager.setLastUser(accountId, deviceNumber);
10720
- await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
10721
- try {
10722
- await getLoginSession(this.context, accountId);
10723
- } catch {}
10724
- this.emit({
10725
- step: 5,
10726
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10727
- status: EmailRecoveryStatus.SUCCESS,
10728
- message: `Welcome ${accountId}`,
10729
- data: { autoLogin: "success" }
10730
- });
10731
- return;
10732
- }
10733
- } catch (err$1) {
10734
- console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
10735
- }
10736
10834
  const authChallenge = createRandomVRFChallenge();
10737
10835
  const storedCredentialId = String(rec.credential?.rawId || rec.credential?.id || "").trim();
10738
10836
  const credentialIds = storedCredentialId ? [storedCredentialId] : [];
@@ -10742,43 +10840,104 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
10742
10840
  challenge: authChallenge,
10743
10841
  credentialIds: credentialIds.length > 0 ? credentialIds : authenticators.map((a) => a.credentialId)
10744
10842
  });
10745
- if (storedCredentialId && authCredential.rawId !== storedCredentialId) throw new Error("Wrong passkey selected during recovery auto-login; please use the newly recovered passkey.");
10843
+ if (storedCredentialId && authCredential.rawId !== storedCredentialId) return {
10844
+ success: false,
10845
+ reason: "Wrong passkey selected during recovery auto-login; please use the newly recovered passkey."
10846
+ };
10746
10847
  const vrfUnlockResult = await webAuthnManager.unlockVRFKeypair({
10747
10848
  nearAccountId: accountId,
10748
10849
  encryptedVrfKeypair: rec.encryptedVrfKeypair,
10749
10850
  credential: authCredential
10750
10851
  });
10751
- if (!vrfUnlockResult.success) throw new Error(vrfUnlockResult.error || "VRF unlock failed during auto-login");
10752
- const vrfStatus = await webAuthnManager.checkVrfStatus();
10753
- const vrfActiveForAccount = vrfStatus.active && vrfStatus.nearAccountId && String(vrfStatus.nearAccountId) === String(accountId);
10754
- if (!vrfActiveForAccount) throw new Error("VRF session inactive after TouchID unlock");
10755
- await webAuthnManager.setLastUser(accountId, deviceNumber);
10756
- await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
10757
- try {
10758
- await getLoginSession(this.context, accountId);
10759
- } catch {}
10760
- this.emit({
10761
- step: 5,
10762
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10763
- status: EmailRecoveryStatus.SUCCESS,
10764
- message: `Welcome ${accountId}`,
10765
- data: { autoLogin: "success" }
10766
- });
10852
+ if (!vrfUnlockResult.success) return {
10853
+ success: false,
10854
+ reason: vrfUnlockResult.error || "VRF unlock failed during auto-login"
10855
+ };
10856
+ await this.assertVrfActiveForAccount(accountId, "VRF session inactive after TouchID unlock");
10857
+ await this.finalizeLocalLoginState(accountId, deviceNumber);
10858
+ return { success: true };
10767
10859
  } catch (err$1) {
10768
- console.warn("[EmailRecoveryFlow] Auto-login failed after recovery", err$1);
10769
- try {
10770
- await this.context.webAuthnManager.clearVrfSession();
10771
- } catch {}
10860
+ return {
10861
+ success: false,
10862
+ reason: err$1?.message || String(err$1)
10863
+ };
10864
+ }
10865
+ }
10866
+ async handleAutoLoginFailure(reason, err$1) {
10867
+ console.warn("[EmailRecoveryFlow] Auto-login failed after recovery", err$1 ?? reason);
10868
+ try {
10869
+ await this.context.webAuthnManager.clearVrfSession();
10870
+ } catch {}
10871
+ return {
10872
+ success: false,
10873
+ reason
10874
+ };
10875
+ }
10876
+ async finalizeRegistration(rec) {
10877
+ this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
10878
+ this.emit({
10879
+ step: 5,
10880
+ phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10881
+ status: EmailRecoveryStatus.PROGRESS,
10882
+ message: "Finalizing email recovery registration...",
10883
+ data: {
10884
+ accountId: rec.accountId,
10885
+ nearPublicKey: rec.nearPublicKey
10886
+ }
10887
+ });
10888
+ try {
10889
+ const { nonceManager, accountId } = this.initializeNonceManager(rec);
10890
+ const signedTx = await this.signRegistrationTx(rec, accountId);
10891
+ const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);
10892
+ if (!txHash) console.warn("[EmailRecoveryFlow] Registration transaction confirmed without hash; continuing local persistence");
10893
+ await this.persistRecoveredUserData(rec, accountId);
10894
+ await this.syncAuthenticatorsBestEffort(accountId);
10895
+ await this.persistAuthenticatorBestEffort(rec, accountId);
10896
+ await this.setLastUserBestEffort(accountId, rec.deviceNumber);
10897
+ await this.updateNonceBestEffort(nonceManager, signedTx);
10898
+ this.emitAutoLoginEvent(EmailRecoveryStatus.PROGRESS, "Attempting auto-login with recovered device...", { autoLogin: "progress" });
10899
+ const autoLoginResult = await this.attemptAutoLogin(rec);
10900
+ if (autoLoginResult.success) this.emitAutoLoginEvent(EmailRecoveryStatus.SUCCESS, `Welcome ${accountId}`, { autoLogin: "success" });
10901
+ else this.emitAutoLoginEvent(EmailRecoveryStatus.ERROR, "Auto-login failed; please log in manually on this device.", {
10902
+ error: autoLoginResult.reason,
10903
+ autoLogin: "error"
10904
+ });
10905
+ await this.markCompleteAndClearPending(rec);
10906
+ this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
10772
10907
  this.emit({
10773
- step: 5,
10774
- phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
10775
- status: EmailRecoveryStatus.ERROR,
10776
- message: "Auto-login failed; please log in manually on this device.",
10908
+ step: 6,
10909
+ phase: EmailRecoveryPhase.STEP_6_COMPLETE,
10910
+ status: EmailRecoveryStatus.SUCCESS,
10911
+ message: "Email recovery completed successfully",
10777
10912
  data: {
10778
- error: err$1?.message || String(err$1),
10779
- autoLogin: "error"
10913
+ accountId: rec.accountId,
10914
+ nearPublicKey: rec.nearPublicKey
10780
10915
  }
10781
10916
  });
10917
+ } catch (e) {
10918
+ const err$1 = this.emitError(5, e?.message || "Email recovery finalization failed");
10919
+ await this.options?.afterCall?.(false);
10920
+ throw err$1;
10921
+ }
10922
+ }
10923
+ async attemptAutoLogin(rec) {
10924
+ try {
10925
+ const accountId = toAccountId(rec.accountId);
10926
+ const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
10927
+ if (deviceNumber === null) return this.handleAutoLoginFailure(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
10928
+ const shamirUnlocked = await this.tryShamirUnlock(rec, accountId, deviceNumber);
10929
+ if (shamirUnlocked) return {
10930
+ success: true,
10931
+ method: "shamir"
10932
+ };
10933
+ const touchIdResult = await this.tryTouchIdUnlock(rec, accountId, deviceNumber);
10934
+ if (touchIdResult.success) return {
10935
+ success: true,
10936
+ method: "touchid"
10937
+ };
10938
+ return this.handleAutoLoginFailure(touchIdResult.reason || "Auto-login failed");
10939
+ } catch (err$1) {
10940
+ return this.handleAutoLoginFailure(err$1?.message || String(err$1), err$1);
10782
10941
  }
10783
10942
  }
10784
10943
  };
@@ -10793,6 +10952,7 @@ init_IndexedDBManager();
10793
10952
  init_actions();
10794
10953
  init_errors();
10795
10954
  init_rpcCalls();
10955
+ init_EmailRecovery();
10796
10956
  init_defaultConfigs();
10797
10957
  let warnedAboutSameOriginWallet = false;
10798
10958
  /**
@@ -10849,7 +11009,7 @@ var TatchiPasskey = class {
10849
11009
  } catch {}
10850
11010
  if (!this.iframeRouter) {
10851
11011
  if (!this.walletIframeInitInFlight) this.walletIframeInitInFlight = (async () => {
10852
- const { WalletIframeRouter } = await import("./router-BLFegW7J.js");
11012
+ const { WalletIframeRouter } = await import("./router-DuGYOd3G.js");
10853
11013
  this.iframeRouter = new WalletIframeRouter({
10854
11014
  walletOrigin,
10855
11015
  servicePath: walletIframeConfig?.walletServicePath || "/wallet-service",