@tatchi-xyz/sdk 0.31.0 → 0.32.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/README.md +2 -0
- package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/cjs/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/cjs/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/login.js +1 -1
- package/dist/cjs/core/TatchiPasskey/login.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/registration.js +107 -63
- package/dist/cjs/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/on-events-progress-bus.js +1 -1
- package/dist/cjs/core/WalletIframe/client/on-events-progress-bus.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/cjs/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/cjs/core/WebAuthnManager/index.js +3 -3
- package/dist/cjs/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/core/defaultConfigs.js +3 -1
- package/dist/cjs/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/core/types/sdkSentEvents.js +3 -2
- package/dist/cjs/core/types/sdkSentEvents.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
- package/dist/cjs/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CRlobBrN.css → PasskeyAuthMenu-D2eRb2-S.css} +3 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2eRb2-S.css.map +1 -0
- package/dist/cjs/react/components/PasskeyAuthMenu/preload.js +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/preload.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/shell.js +52 -13
- package/dist/cjs/react/components/PasskeyAuthMenu/shell.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js +4 -2
- package/dist/cjs/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js +1 -1
- package/dist/cjs/react/context/useTatchiWithSdkFlow.js.map +1 -1
- package/dist/cjs/react/index.js +1 -1
- package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/cjs/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/cjs/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/login.js +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/login.js.map +1 -1
- package/dist/cjs/react/src/core/TatchiPasskey/registration.js +107 -63
- package/dist/cjs/react/src/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/cjs/react/src/core/WalletIframe/client/on-events-progress-bus.js +1 -1
- package/dist/cjs/react/src/core/WalletIframe/client/on-events-progress-bus.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/cjs/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/cjs/react/src/core/WebAuthnManager/index.js +3 -3
- package/dist/cjs/react/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/cjs/react/src/core/defaultConfigs.js +3 -1
- package/dist/cjs/react/src/core/defaultConfigs.js.map +1 -1
- package/dist/cjs/react/src/core/types/sdkSentEvents.js +3 -2
- package/dist/cjs/react/src/core/types/sdkSentEvents.js.map +1 -1
- package/dist/cjs/server/core/AuthService.js +49 -6
- package/dist/cjs/server/core/AuthService.js.map +1 -1
- package/dist/cjs/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/esm/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/esm/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/login.js +1 -1
- package/dist/esm/core/TatchiPasskey/login.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/registration.js +107 -63
- package/dist/esm/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/on-events-progress-bus.js +1 -1
- package/dist/esm/core/WalletIframe/client/on-events-progress-bus.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/esm/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/esm/core/WebAuthnManager/index.js +3 -3
- package/dist/esm/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/core/defaultConfigs.js +3 -1
- package/dist/esm/core/defaultConfigs.js.map +1 -1
- package/dist/esm/core/types/sdkSentEvents.js +3 -2
- package/dist/esm/core/types/sdkSentEvents.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js +3 -3
- package/dist/esm/react/components/AccountMenuButton/TransactionSettingsSection.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-D2VHZ04W.css → PasskeyAuthMenu-qTHAv58Z.css} +3 -1
- package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-qTHAv58Z.css.map +1 -0
- package/dist/esm/react/components/PasskeyAuthMenu/preload.js +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/preload.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/shell.js +52 -13
- package/dist/esm/react/components/PasskeyAuthMenu/shell.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js +4 -2
- package/dist/esm/react/components/PasskeyAuthMenu/skeleton.js.map +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js +5 -1
- package/dist/esm/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.js.map +1 -1
- package/dist/esm/react/context/useTatchiWithSdkFlow.js +1 -1
- package/dist/esm/react/context/useTatchiWithSdkFlow.js.map +1 -1
- package/dist/esm/react/index.js +1 -1
- package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js +2 -2
- package/dist/esm/react/src/core/IndexedDBManager/passkeyClientDB.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js +9 -8
- package/dist/esm/react/src/core/TatchiPasskey/faucets/createAccountRelayServer.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/login.js +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/login.js.map +1 -1
- package/dist/esm/react/src/core/TatchiPasskey/registration.js +107 -63
- package/dist/esm/react/src/core/TatchiPasskey/registration.js.map +1 -1
- package/dist/esm/react/src/core/WalletIframe/client/on-events-progress-bus.js +1 -1
- package/dist/esm/react/src/core/WalletIframe/client/on-events-progress-bus.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js +1 -10
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js +58 -67
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js +74 -75
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js +17 -7
- package/dist/esm/react/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.js.map +1 -1
- package/dist/esm/react/src/core/WebAuthnManager/index.js +3 -3
- package/dist/esm/react/src/core/WebAuthnManager/index.js.map +1 -1
- package/dist/esm/react/src/core/defaultConfigs.js +3 -1
- package/dist/esm/react/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/react/src/core/types/sdkSentEvents.js +3 -2
- package/dist/esm/react/src/core/types/sdkSentEvents.js.map +1 -1
- package/dist/esm/react/styles/styles.css +2 -0
- package/dist/esm/sdk/{EmailRecovery-Dl8b4ONg.js → EmailRecovery-Y7rurd4B.js} +3 -3
- package/dist/esm/sdk/{EmailRecovery-v9oNO2Tc.js → EmailRecovery-lsjLWApQ.js} +1 -1
- package/dist/esm/sdk/{IndexedDBManager-B1cUvdyY.js → IndexedDBManager-CmdN7smS.js} +3 -3
- package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js → createAdapters-4c8mBiD5.js} +2 -11
- package/dist/esm/sdk/{createAdapters-Dv7ZJPf1.js.map → createAdapters-4c8mBiD5.js.map} +1 -1
- package/dist/esm/sdk/{createAdapters-1Hmc1vVC.js → createAdapters-DF32SIZa.js} +1 -10
- package/dist/esm/sdk/{defaultConfigs-BmCU1_qI.js → defaultConfigs-BQqiXif-.js} +3 -1
- package/dist/esm/sdk/{delegateAction-DdkvFFKA.js → delegateAction-Bq5zkOvn.js} +1 -1
- package/dist/esm/sdk/{emailRecovery-4J-g9tlY.js → emailRecovery-B1hbE_sM.js} +6 -6
- package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js → getDeviceNumber-WiNzKx1x.js} +4 -2
- package/dist/esm/sdk/{getDeviceNumber-f8bfPB9U.js.map → getDeviceNumber-WiNzKx1x.js.map} +1 -1
- package/dist/esm/sdk/{linkDevice-C98klpcE.js → linkDevice-CRPf5aW2.js} +5 -5
- package/dist/esm/sdk/{localOnly-40zxrBMm.js → localOnly-COpDBMkm.js} +2 -2
- package/dist/esm/sdk/{localOnly-40zxrBMm.js.map → localOnly-COpDBMkm.js.map} +1 -1
- package/dist/esm/sdk/{localOnly-BZPBj14l.js → localOnly-DQQuqgjJ.js} +1 -1
- package/dist/esm/sdk/{login-DnROv3eA.js → login-DUIWZHp_.js} +4 -4
- package/dist/esm/sdk/offline-export-app.js +32 -21
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{registration-BP9M3tE1.js → registration-BR2G9tz_.js} +59 -68
- package/dist/esm/sdk/{registration-MrAOC8Ub.js → registration-R70lvG_o.js} +60 -69
- package/dist/esm/sdk/registration-R70lvG_o.js.map +1 -0
- package/dist/esm/sdk/{relay-Dq9D7fhG.js → relay-BCEyWFew.js} +1 -1
- package/dist/esm/sdk/{router-BEGGuWaB.js → router-Cj2WexK-.js} +3 -3
- package/dist/esm/sdk/{rpcCalls-CMzj_Va_.js → rpcCalls-C1sp-Epo.js} +3 -3
- package/dist/esm/sdk/{rpcCalls-B44MZora.js → rpcCalls-VL4loDKP.js} +2 -2
- package/dist/esm/sdk/{scanDevice-Cp-r-Z2T.js → scanDevice-C0HcnZym.js} +5 -5
- package/dist/esm/sdk/{sdkSentEvents-CzAZBFjP.js → sdkSentEvents-BfkcI7EN.js} +3 -2
- package/dist/esm/sdk/{signNEP413-DsyWH_Jo.js → signNEP413-lj0swHsD.js} +1 -1
- package/dist/esm/sdk/{syncAccount-CqWCmBVb.js → syncAccount-DnQ9AstS.js} +5 -5
- package/dist/esm/sdk/{syncAccount-Dt5jJbEB.js → syncAccount-xh81Vppo.js} +3 -3
- package/dist/esm/sdk/{transactions-DAZrPW-6.js → transactions-Cg1TIUyK.js} +76 -77
- package/dist/esm/sdk/{transactions-CrjP8yPD.js → transactions-CxsklyCK.js} +77 -78
- package/dist/esm/sdk/transactions-CxsklyCK.js.map +1 -0
- package/dist/esm/sdk/wallet-iframe-host.js +160 -105
- package/dist/esm/server/core/AuthService.js +49 -6
- package/dist/esm/server/core/AuthService.js.map +1 -1
- package/dist/esm/server/sdk/src/core/defaultConfigs.js.map +1 -1
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/__tests__/setup/bootstrap.d.ts.map +1 -1
- package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts +1 -1
- package/dist/types/src/core/IndexedDBManager/passkeyClientDB.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts +6 -6
- package/dist/types/src/core/TatchiPasskey/faucets/createAccountRelayServer.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/registration.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts +0 -5
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/adapters/session.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/registration.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/flows/transactions.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/VrfWorkerManager/confirmTxFlow/handleSecureConfirmRequest.d.ts.map +1 -1
- package/dist/types/src/core/WebAuthnManager/index.d.ts +1 -1
- package/dist/types/src/core/WebAuthnManager/index.d.ts.map +1 -1
- package/dist/types/src/core/defaultConfigs.d.ts.map +1 -1
- package/dist/types/src/core/types/sdkSentEvents.d.ts +18 -7
- package/dist/types/src/core/types/sdkSentEvents.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/preload.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/shell.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/skeleton.d.ts.map +1 -1
- package/dist/types/src/react/components/PasskeyAuthMenu/ui/EmailRecoverySlide.d.ts.map +1 -1
- package/dist/types/src/server/core/AuthService.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +4 -4
- package/dist/cjs/react/components/PasskeyAuthMenu/PasskeyAuthMenu-CRlobBrN.css.map +0 -1
- package/dist/esm/react/components/PasskeyAuthMenu/PasskeyAuthMenu-D2VHZ04W.css.map +0 -1
- package/dist/esm/sdk/registration-MrAOC8Ub.js.map +0 -1
- package/dist/esm/sdk/transactions-CrjP8yPD.js.map +0 -1
package/README.md
CHANGED
|
@@ -68,6 +68,8 @@ pnpm -C sdk run type-check # TypeScript validation
|
|
|
68
68
|
|
|
69
69
|
The easiest way to get started with React:
|
|
70
70
|
|
|
71
|
+
Requires React 18+ (React 18 or 19). The core SDK works without React.
|
|
72
|
+
|
|
71
73
|
```tsx
|
|
72
74
|
import { PasskeyProvider, usePasskeyManager, PASSKEY_MANAGER_DEFAULT_CONFIGS } from '@tatchi-xyz/sdk/react'
|
|
73
75
|
|
|
@@ -300,8 +300,8 @@ var PasskeyClientDBManager = class {
|
|
|
300
300
|
await this.storeUser(userData);
|
|
301
301
|
return userData;
|
|
302
302
|
}
|
|
303
|
-
async updateUser(nearAccountId, updates) {
|
|
304
|
-
const user = await this.getUser(nearAccountId);
|
|
303
|
+
async updateUser(nearAccountId, updates, deviceNumber) {
|
|
304
|
+
const user = await this.getUser(nearAccountId, deviceNumber);
|
|
305
305
|
if (user) {
|
|
306
306
|
const updatedUser = {
|
|
307
307
|
...user,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"passkeyClientDB.js","names":["DB_CONFIG: PasskeyClientDBConfig","err: any","entry: AppStateEntry<T>","validateNearAccountId","toAccountId","userData: ClientUserData","DEFAULT_CONFIRMATION_CONFIG","lastUserState: LastUserAccountIdState","fixed: ClientUserData","updatedUser: ClientUserData","clientAuth: ClientAuthenticatorData","coerceSignerMode","DEFAULT_SIGNING_MODE","rec: DerivedAddressRecord","rec: RecoveryEmailRecord"],"sources":["../../../../src/core/IndexedDBManager/passkeyClientDB.ts"],"sourcesContent":["import { openDB, type IDBPDatabase } from 'idb';\nimport { type ValidationResult, validateNearAccountId } from '../../utils/validation';\nimport type { AccountId } from '../types/accountIds';\nimport { toAccountId } from '../types/accountIds';\nimport {\n ConfirmationConfig,\n DEFAULT_CONFIRMATION_CONFIG,\n type SignerMode,\n DEFAULT_SIGNING_MODE,\n coerceSignerMode,\n} from '../types/signer-worker'\n\n\nexport interface ClientUserData {\n // Primary key - now uses AccountId + deviceNumber for unique identification\n nearAccountId: AccountId;\n deviceNumber: number; // Device number for multi-device support (1-indexed)\n version?: number;\n\n // User metadata\n registeredAt?: number;\n lastLogin?: number;\n lastUpdated?: number;\n\n // WebAuthn/Passkey data (merged from WebAuthnManager)\n clientNearPublicKey: string;\n passkeyCredential: {\n id: string;\n rawId: string;\n };\n\n // VRF credentials for stateless authentication\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: string;\n chacha20NonceB64u: string;\n };\n // Server-assisted auto-login (VRF key session): Shamir 3-pass fields\n // Stores relayer-blinded KEK and the VRF ciphertext; server never sees plaintext VRF or KEK\n serverEncryptedVrfKeypair?: {\n ciphertextVrfB64u: string;\n kek_s_b64u: string;\n // Metadata for proactive refresh\n serverKeyId: string;\n updatedAt?: number;\n };\n\n // User preferences\n preferences?: UserPreferences;\n}\n\nexport type StoreUserDataInput = Omit<ClientUserData, 'deviceNumber' | 'lastLogin' | 'registeredAt'>\n & {\n deviceNumber?: number;\n serverEncryptedVrfKeypair?: ClientUserData['serverEncryptedVrfKeypair'];\n version?: number;\n };\n\nexport type StoreWebAuthnUserDataInput = {\n nearAccountId: AccountId;\n deviceNumber: number;\n clientNearPublicKey: string;\n lastUpdated?: number;\n version?: number;\n passkeyCredential: ClientUserData['passkeyCredential'];\n encryptedVrfKeypair: ClientUserData['encryptedVrfKeypair'];\n serverEncryptedVrfKeypair?: ClientUserData['serverEncryptedVrfKeypair'];\n};\n\nexport interface UserPreferences {\n useRelayer: boolean;\n useNetwork: 'testnet' | 'mainnet';\n confirmationConfig: ConfirmationConfig;\n signerMode?: SignerMode;\n // User preferences can be extended here as needed\n}\n\n// Authenticator cache\nexport interface ClientAuthenticatorData {\n credentialId: string;\n credentialPublicKey: Uint8Array;\n transports?: string[]; // AuthenticatorTransport[]\n name?: string;\n nearAccountId: AccountId; // FK reference using AccountId\n deviceNumber: number; // Device number for this authenticator (1-indexed)\n registered: string; // ISO date string\n syncedAt: string; // When this cache entry was last synced with contract\n vrfPublicKey: string; // Base64-encoded VRF public key (1:1 relationship on client)\n}\n\ninterface AppStateEntry<T = unknown> {\n key: string;\n value: T;\n}\n\n// Internal helper: legacy user records may be missing deviceNumber.\ntype ClientUserDataWithOptionalDevice =\n | ClientUserData\n | (Omit<ClientUserData, 'deviceNumber'> & { deviceNumber?: number });\n\n// Special type for lastUserAccountId app state entry\nexport interface LastUserAccountIdState {\n accountId: AccountId;\n deviceNumber: number;\n}\n\ninterface PasskeyClientDBConfig {\n dbName: string;\n dbVersion: number;\n userStore: string;\n appStateStore: string;\n authenticatorStore: string;\n derivedAddressStore: string;\n recoveryEmailStore: string;\n}\n\n// === CONSTANTS ===\nconst DB_CONFIG: PasskeyClientDBConfig = {\n dbName: 'PasskeyClientDB',\n dbVersion: 15, // v15: add recoveryEmails store\n userStore: 'users',\n appStateStore: 'appState',\n authenticatorStore: 'authenticators',\n derivedAddressStore: 'derivedAddresses',\n recoveryEmailStore: 'recoveryEmails'\n} as const;\n\nexport interface IndexedDBEvent {\n type: 'user-updated' | 'preferences-updated' | 'user-deleted';\n accountId: AccountId;\n data?: Record<string, unknown>;\n}\n\n// Persisted mapping of derived (e.g., EVM) addresses tied to an account\n/**\n * Persisted mapping of derived (e.g., EVM/Solana/Zcash) addresses tied to an account.\n *\n * Notes on multi-chain support:\n * - The composite primary key is [nearAccountId, contractId, path]. To support\n * different chains and chain IDs, encode them in the `path` string, e.g.:\n * - EVM: `evm:<chainId>:<derivationPath>` → `evm:84532:ethereum-1`\n * - Solana: `solana:<derivationPath>`\n * - Zcash: `zcash:<derivationPath>`\n * - Additional descriptive fields like `namespace` and `chainRef` are optional metadata\n * and are not part of the key.\n */\nexport interface DerivedAddressRecord {\n nearAccountId: AccountId;\n contractId: string; // MPC/Derivation contract on NEAR\n path: string; // Composite path (may include namespace/chainId); see docs above\n address: string; // Derived address (e.g., 0x...)\n updatedAt: number;\n // Optional metadata (not used in the key)\n namespace?: string; // e.g., 'evm', 'solana', 'zcash'\n chainRef?: string; // e.g., chainId '84532' or a named network slug\n}\n\n/**\n * Persisted mapping of recovery email hashes to canonical email addresses for an account.\n *\n * Notes:\n * - Composite primary key is [nearAccountId, hashHex].\n * - `hashHex` is the 0x-prefixed hex encoding of the 32-byte hash:\n * SHA256(canonical_email || \"|\" || account_id)\n * - `email` is the canonical form: \"local@domain\", lowercased.\n */\nexport interface RecoveryEmailRecord {\n nearAccountId: AccountId;\n hashHex: string;\n email: string;\n addedAt: number;\n}\n\nexport class PasskeyClientDBManager {\n private config: PasskeyClientDBConfig;\n private db: IDBPDatabase | null = null;\n private disabled = false;\n private eventListeners: Set<(event: IndexedDBEvent) => void> = new Set();\n\n constructor(config: PasskeyClientDBConfig = DB_CONFIG) {\n this.config = config;\n }\n\n getDbName(): string {\n return this.config.dbName;\n }\n\n setDbName(dbName: string): void {\n const next = String(dbName || '').trim();\n if (!next || next === this.config.dbName) return;\n try { (this.db as any)?.close?.(); } catch {}\n this.db = null;\n this.config = { ...this.config, dbName: next };\n }\n\n isDisabled(): boolean {\n return this.disabled;\n }\n\n setDisabled(disabled: boolean): void {\n const next = !!disabled;\n if (next === this.disabled) return;\n this.disabled = next;\n if (next) {\n try { (this.db as any)?.close?.(); } catch {}\n this.db = null;\n }\n }\n\n // === EVENT SYSTEM ===\n\n onChange(listener: (event: IndexedDBEvent) => void): () => void {\n this.eventListeners.add(listener);\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n private emitEvent(event: IndexedDBEvent): void {\n this.eventListeners.forEach(listener => {\n try {\n listener(event);\n } catch (error) {\n console.warn('[IndexedDBManager]: Error in event listener:', error);\n }\n });\n }\n\n private async getDB(): Promise<IDBPDatabase> {\n if (this.disabled) {\n throw new Error('[PasskeyClientDBManager] IndexedDB is disabled in this environment.');\n }\n if (this.db) {\n return this.db;\n }\n\n try {\n this.db = await openDB(this.config.dbName, this.config.dbVersion, {\n upgrade: (db, oldVersion, _newVersion, _transaction): void => {\n // Create stores if they don't exist\n if (!db.objectStoreNames.contains(DB_CONFIG.userStore)) {\n // Users table: composite key of [nearAccountId, deviceNumber]\n const userStore = db.createObjectStore(DB_CONFIG.userStore, { keyPath: ['nearAccountId', 'deviceNumber'] });\n userStore.createIndex('nearAccountId', 'nearAccountId', { unique: false });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.appStateStore)) {\n db.createObjectStore(DB_CONFIG.appStateStore, { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.authenticatorStore)) {\n // Authenticators table: composite key of [nearAccountId, deviceNumber, credentialId]\n const authStore = db.createObjectStore(DB_CONFIG.authenticatorStore, { keyPath: ['nearAccountId', 'deviceNumber', 'credentialId'] });\n authStore.createIndex('nearAccountId', 'nearAccountId', { unique: false });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.derivedAddressStore)) {\n // Derived addresses: composite key of [nearAccountId, contractId, path]\n const dStore = db.createObjectStore(DB_CONFIG.derivedAddressStore, { keyPath: ['nearAccountId', 'contractId', 'path'] });\n try { dStore.createIndex('nearAccountId', 'nearAccountId', { unique: false }); } catch {}\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.recoveryEmailStore)) {\n // Recovery emails: composite key of [nearAccountId, hashHex]\n const rStore = db.createObjectStore(DB_CONFIG.recoveryEmailStore, { keyPath: ['nearAccountId', 'hashHex'] });\n try { rStore.createIndex('nearAccountId', 'nearAccountId', { unique: false }); } catch {}\n }\n },\n blocked() {\n console.warn('PasskeyClientDB connection is blocked.');\n },\n blocking() {\n console.warn('PasskeyClientDB connection is blocking another connection.');\n },\n terminated: () => {\n console.warn('PasskeyClientDB connection has been terminated.');\n this.db = null;\n },\n });\n\n // Post-open migrations (non-blocking)\n try { await this.runMigrationsIfNeeded(this.db); } catch {}\n\n } catch (err: any) {\n const msg = String(err?.message || '');\n if (err?.name === 'VersionError' || /less than the existing version/i.test(msg)) {\n // Mixed-version contexts (host/app) — open without version to adopt existing DB\n try {\n console.warn('PasskeyClientDB: opening existing DB without version due to VersionError');\n this.db = await openDB(this.config.dbName);\n } catch (e) {\n throw err;\n }\n } else {\n throw err;\n }\n }\n\n return this.db;\n }\n\n private async runMigrationsIfNeeded(_db: IDBPDatabase): Promise<void> {\n return;\n }\n\n // === APP STATE METHODS ===\n\n async getAppState<T = unknown>(key: string): Promise<T | undefined> {\n const db = await this.getDB();\n const result = await db.get(DB_CONFIG.appStateStore, key);\n return result?.value as T | undefined;\n }\n\n async setAppState<T = unknown>(key: string, value: T): Promise<void> {\n const db = await this.getDB();\n const entry: AppStateEntry<T> = { key, value };\n await db.put(DB_CONFIG.appStateStore, entry);\n }\n\n // === ACCOUNT ID VALIDATION AND UTILITIES ===\n\n /**\n * Validate that a NEAR account ID is in the expected format\n * Supports both <username>.<relayerAccountId> and <username>.testnet formats\n */\n validateNearAccountId(nearAccountId: AccountId): ValidationResult {\n return validateNearAccountId(nearAccountId);\n }\n\n /**\n * Extract username from NEAR account ID\n */\n extractUsername(nearAccountId: AccountId): string {\n const validation = validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n throw new Error(`Invalid NEAR account ID: ${validation.error}`);\n }\n return nearAccountId.split('.')[0];\n }\n\n /**\n * Generate a NEAR account ID from a username and domain\n * @param username - The username to use for the account ID\n * @param domain - The domain to use for the account ID\n * @returns The generated NEAR account ID\n */\n generateNearAccountId(username: string, domain: string): string {\n const sanitizedName = username\n .toLowerCase()\n .replace(/[^a-z0-9_\\\\-]/g, '')\n .substring(0, 32);\n return `${sanitizedName}.${domain}`;\n }\n\n // === USER MANAGEMENT METHODS ===\n\n async getUser(nearAccountId: AccountId, deviceNumber?: number): Promise<ClientUserData | null> {\n if (!nearAccountId) return null;\n\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n console.warn(`Invalid account ID format: ${nearAccountId}`);\n return null;\n }\n\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n\n if (typeof deviceNumber === 'number') {\n const rec = await db.get(DB_CONFIG.userStore, [accountId, deviceNumber]);\n if (!rec) return null;\n return await this.normalizeUserDeviceNumber(rec as ClientUserDataWithOptionalDevice, deviceNumber);\n }\n\n const index = db.transaction(DB_CONFIG.userStore).store.index('nearAccountId');\n const results = await index.getAll(accountId);\n if (results.length === 0) {\n return null;\n }\n\n if (results.length > 1) {\n console.warn(\n `Multiple passkeys found for account ${accountId}, deviceNumber not provided; ` +\n 'defaulting to last logged-in user.'\n );\n console.log('defaulting to last used user deviceNumber');\n const lastUserState = await this.getAppState<LastUserAccountIdState>('lastUserAccountId').catch(() => null);\n if (lastUserState && toAccountId(lastUserState.accountId) === accountId) {\n const keyed = await db.get(DB_CONFIG.userStore, [accountId, lastUserState.deviceNumber]);\n if (keyed) {\n return await this.normalizeUserDeviceNumber(\n keyed as ClientUserDataWithOptionalDevice,\n lastUserState.deviceNumber\n );\n }\n }\n }\n\n const first = results[0] as ClientUserDataWithOptionalDevice;\n if (!first) return null;\n return await this.normalizeUserDeviceNumber(first, 1);\n }\n\n /**\n * Get the current/last user\n * This is maintained via app state and updated whenever a user is stored or updated\n */\n async getLastUser(): Promise<ClientUserData | null> {\n const lastUserState = await this.getAppState<LastUserAccountIdState>('lastUserAccountId');\n if (!lastUserState) return null;\n const db = await this.getDB();\n const accountId = toAccountId(lastUserState.accountId);\n // Prefer exact device match using composite primary key\n const record = await db.get(DB_CONFIG.userStore, [accountId, lastUserState.deviceNumber]);\n if (record) return record as ClientUserData;\n // Fallback: return any user for account\n return this.getUser(accountId);\n }\n\n /** Get user record by composite key (nearAccountId, deviceNumber) */\n async getUserByDevice(nearAccountId: AccountId, deviceNumber: number): Promise<ClientUserData | null> {\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const rec = await db.get(DB_CONFIG.userStore, [accountId, deviceNumber]);\n return rec as ClientUserData || null;\n }\n\n /**\n * Get the most recently updated user record for a given account.\n * Useful when deviceNumber is unknown but we need the freshest key for the account.\n */\n /**\n * Get the most recently updated user record for a given account.\n * Useful when deviceNumber is unknown but we need the freshest key for the account.\n */\n async getLastDBUpdatedUser(nearAccountId: AccountId): Promise<ClientUserData | null> {\n const db = await this.getDB();\n try {\n const idx = db.transaction(DB_CONFIG.userStore).store.index('nearAccountId');\n const all = await idx.getAll(toAccountId(nearAccountId));\n if (Array.isArray(all) && all.length > 0) {\n const latest = (all as ClientUserData[]).reduce((a, b) =>\n (a.lastUpdated ?? 0) >= (b.lastUpdated ?? 0) ? a : b\n );\n return latest;\n }\n } catch {\n // fall through\n }\n return null;\n }\n\n async hasPasskeyCredential(nearAccountId: AccountId): Promise<boolean> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n return !!authenticators[0]?.credentialId;\n }\n\n /**\n * Ensure the current passkey selection is aligned with the last logged-in device.\n *\n * - When multiple authenticators exist for an account and no deviceNumber is specified,\n * this helper prefers authenticators whose deviceNumber matches the last logged-in user.\n * - Optionally validates that a selected credential (by rawId) also matches the last-user device.\n *\n * @param nearAccountId - Account ID for which the operation is being performed\n * @param authenticators - All authenticators stored for the account\n * @param selectedCredentialRawId - Optional rawId of the credential chosen by WebAuthn\n * @returns filtered authenticators for allowCredentials, plus optional wrongPasskeyError\n */\n async ensureCurrentPasskey(\n nearAccountId: AccountId,\n authenticators: ClientAuthenticatorData[],\n selectedCredentialRawId?: string,\n ): Promise<{\n authenticatorsForPrompt: ClientAuthenticatorData[];\n wrongPasskeyError?: string;\n }> {\n if (authenticators.length <= 1) {\n return { authenticatorsForPrompt: authenticators };\n }\n\n const accountIdNormalized = toAccountId(nearAccountId);\n const lastUser = await this.getLastUser().catch(() => null);\n if (!lastUser || lastUser.nearAccountId !== accountIdNormalized) {\n return { authenticatorsForPrompt: authenticators };\n }\n\n const expectedDeviceNumber = lastUser.deviceNumber;\n const byDeviceNumber = authenticators.filter(a => a.deviceNumber === expectedDeviceNumber);\n\n // Prefer the credentialId for the last-user deviceNumber; use the stored last-user rawId\n // only when it matches an authenticator for that device (or when we have no device match).\n let expectedCredentialId = lastUser.passkeyCredential.rawId;\n if (byDeviceNumber.length > 0 && !byDeviceNumber.some(a => a.credentialId === expectedCredentialId)) {\n expectedCredentialId = byDeviceNumber[0].credentialId;\n }\n\n // Preference: restrict allowCredentials to the last-user credentialId.\n // Fallback: if the local authenticator cache is missing that entry, prefer the last-user deviceNumber.\n const byCredentialId = authenticators.filter(a => a.credentialId === expectedCredentialId);\n const authenticatorsForPrompt =\n byCredentialId.length > 0\n ? byCredentialId\n : (byDeviceNumber.length > 0 ? byDeviceNumber : authenticators);\n\n const wrongPasskeyError =\n selectedCredentialRawId && selectedCredentialRawId !== expectedCredentialId\n ? (\n `You have multiple passkeys (deviceNumbers) for account ${accountIdNormalized}, ` +\n 'but used a different passkey than the most recently logged-in one. Please use the passkey for the most recently logged-in device.'\n )\n : undefined;\n\n return { authenticatorsForPrompt, wrongPasskeyError };\n }\n\n /**\n * Register a new user with the given NEAR account ID\n * @param nearAccountId - Full NEAR account ID (e.g., \"username.testnet\" or \"username.relayer.testnet\")\n * @param additionalData - Additional user data to store\n */\n async registerUser(storeUserData: StoreUserDataInput): Promise<ClientUserData> {\n\n const validation = this.validateNearAccountId(storeUserData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot register user with invalid account ID: ${validation.error}`);\n }\n\n const now = Date.now();\n\n const userData: ClientUserData = {\n nearAccountId: toAccountId(storeUserData.nearAccountId),\n deviceNumber: storeUserData.deviceNumber || 1, // Default to device 1 (1-indexed)\n version: storeUserData.version || 2,\n registeredAt: now,\n lastLogin: now,\n lastUpdated: now,\n clientNearPublicKey: storeUserData.clientNearPublicKey,\n passkeyCredential: storeUserData.passkeyCredential,\n preferences: {\n useRelayer: false,\n useNetwork: 'testnet',\n confirmationConfig: DEFAULT_CONFIRMATION_CONFIG,\n // Default preferences can be set here\n },\n encryptedVrfKeypair: storeUserData.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: storeUserData.serverEncryptedVrfKeypair,\n };\n\n await this.storeUser(userData);\n return userData;\n }\n\n async updateUser(nearAccountId: AccountId, updates: Partial<ClientUserData>): Promise<void> {\n const user = await this.getUser(nearAccountId);\n if (user) {\n const updatedUser = {\n ...user,\n ...updates,\n lastUpdated: Date.now()\n };\n await this.storeUser(updatedUser); // This will update the app state lastUserAccountId\n\n // Emit event for user updates\n this.emitEvent({\n type: 'user-updated',\n accountId: nearAccountId,\n data: { updates, updatedUser }\n });\n }\n }\n\n async updateLastLogin(nearAccountId: AccountId): Promise<void> {\n await this.updateUser(nearAccountId, { lastLogin: Date.now() });\n }\n\n /**\n * Set the last logged-in user\n * @param nearAccountId - The account ID of the user\n * @param deviceNumber - The device number (defaults to 1)\n */\n async setLastUser(nearAccountId: AccountId, deviceNumber: number = 1): Promise<void> {\n const lastUserState: LastUserAccountIdState = {\n accountId: nearAccountId,\n deviceNumber,\n };\n await this.setAppState('lastUserAccountId', lastUserState);\n }\n\n async updatePreferences(\n nearAccountId: AccountId,\n preferences: Partial<UserPreferences>\n ): Promise<void> {\n const user = await this.getUser(nearAccountId);\n if (user) {\n const updatedPreferences = {\n ...user.preferences,\n ...preferences\n } as UserPreferences;\n await this.updateUser(nearAccountId, { preferences: updatedPreferences });\n\n // Emit event for preference changes\n this.emitEvent({\n type: 'preferences-updated',\n accountId: nearAccountId,\n data: { preferences: updatedPreferences }\n });\n }\n }\n\n private async normalizeUserDeviceNumber(\n user: ClientUserDataWithOptionalDevice,\n defaultDeviceNumber: number\n ): Promise<ClientUserData> {\n const hasValidDevice =\n typeof user.deviceNumber === 'number' && Number.isFinite(user.deviceNumber);\n if (hasValidDevice) {\n return user as ClientUserData;\n }\n\n const deviceNumber = defaultDeviceNumber;\n const fixed: ClientUserData = {\n ...(user as Omit<ClientUserData, 'deviceNumber'>),\n deviceNumber,\n };\n await this.storeUser(fixed);\n return fixed;\n }\n\n private async storeUser(userData: ClientUserData): Promise<void> {\n const validation = this.validateNearAccountId(userData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot store user with invalid account ID: ${validation.error}`);\n }\n\n const db = await this.getDB();\n await db.put(DB_CONFIG.userStore, userData);\n\n // Update lastUserAccountId with new format including device info\n const lastUserState: LastUserAccountIdState = {\n accountId: userData.nearAccountId,\n deviceNumber: userData.deviceNumber,\n };\n\n await this.setAppState('lastUserAccountId', lastUserState);\n }\n\n /**\n * Store WebAuthn user data (compatibility with WebAuthnManager)\n * @param userData - User data with nearAccountId as primary identifier\n */\n async storeWebAuthnUserData(userData: StoreWebAuthnUserDataInput): Promise<void> {\n const validation = this.validateNearAccountId(userData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot store WebAuthn data for invalid account ID: ${validation.error}`);\n }\n\n const accountId = toAccountId(userData.nearAccountId);\n const deviceNumber = userData.deviceNumber;\n let user = await this.getUser(accountId, deviceNumber);\n\n if (!user) {\n user = await this.registerUser({\n nearAccountId: accountId,\n deviceNumber,\n clientNearPublicKey: userData.clientNearPublicKey,\n passkeyCredential: userData.passkeyCredential,\n encryptedVrfKeypair: userData.encryptedVrfKeypair,\n version: userData.version || 2,\n serverEncryptedVrfKeypair: userData.serverEncryptedVrfKeypair,\n });\n }\n\n const updatedUser: ClientUserData = {\n ...user,\n clientNearPublicKey: userData.clientNearPublicKey,\n passkeyCredential: userData.passkeyCredential,\n encryptedVrfKeypair: userData.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: userData.serverEncryptedVrfKeypair ?? user.serverEncryptedVrfKeypair,\n version: userData.version ?? user.version,\n lastUpdated: userData.lastUpdated ?? Date.now(),\n };\n\n await this.storeUser(updatedUser);\n this.emitEvent({\n type: 'user-updated',\n accountId,\n data: { updatedUser }\n });\n }\n\n async getAllUsers(): Promise<ClientUserData[]> {\n const db = await this.getDB();\n return db.getAll(DB_CONFIG.userStore);\n }\n\n async deleteUser(nearAccountId: AccountId): Promise<void> {\n const db = await this.getDB();\n await db.delete(DB_CONFIG.userStore, nearAccountId);\n // Also clean up related authenticators\n await this.clearAuthenticatorsForUser(nearAccountId);\n }\n\n async clearAllUsers(): Promise<void> {\n const db = await this.getDB();\n await db.clear(DB_CONFIG.userStore);\n }\n\n async clearAllAppState(): Promise<void> {\n const db = await this.getDB();\n await db.clear(DB_CONFIG.appStateStore);\n }\n\n /**\n * Store authenticator data for a user\n */\n async storeAuthenticator(authenticatorData: ClientAuthenticatorData): Promise<void> {\n const db = await this.getDB();\n await db.put(DB_CONFIG.authenticatorStore, authenticatorData);\n }\n\n /**\n * Get all authenticators for a user (optionally for a specific device)\n */\n async getAuthenticatorsByUser(nearAccountId: AccountId): Promise<ClientAuthenticatorData[]> {\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n const accountId = toAccountId(nearAccountId);\n\n // Get all authenticators for this account across all devices\n const index = store.index('nearAccountId');\n return await index.getAll(accountId);\n }\n\n /**\n * Get a specific authenticator by credential ID\n */\n async getAuthenticatorByCredentialId(\n nearAccountId: AccountId,\n credentialId: string\n ): Promise<ClientAuthenticatorData | null> {\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n const accountId = toAccountId(nearAccountId);\n\n // Primary key is [nearAccountId, deviceNumber, credentialId], so we cannot\n // look up by [nearAccountId, credentialId] directly. Use the nearAccountId\n // index and filter by credentialId.\n const index = store.index('nearAccountId');\n const all = await index.getAll(accountId);\n const match = all.find((auth: any) => auth.credentialId === credentialId) || null;\n return match;\n }\n\n /**\n * Clear all authenticators for a user\n */\n async clearAuthenticatorsForUser(nearAccountId: AccountId): Promise<void> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readwrite');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n\n for (const auth of authenticators) {\n // Composite PK is [nearAccountId, deviceNumber, credentialId]\n await store.delete([nearAccountId, auth.deviceNumber, auth.credentialId]);\n }\n }\n\n /**\n * Sync authenticators from contract data\n */\n async syncAuthenticatorsFromContract(\n nearAccountId: AccountId,\n contractAuthenticators: Array<{\n credentialId: string;\n credentialPublicKey: Uint8Array;\n transports?: string[];\n name?: string;\n registered: string;\n vrfPublicKey: string;\n deviceNumber?: number; // Device number from contract\n }>\n ): Promise<void> {\n // Clear existing cache for this user\n await this.clearAuthenticatorsForUser(nearAccountId);\n\n // Add all contract authenticators to cache\n const syncedAt = new Date().toISOString();\n for (const auth of contractAuthenticators) {\n // Fix transport processing: filter out undefined values and provide fallback\n const rawTransports = auth.transports || [];\n const validTransports = rawTransports.filter((transport: any) =>\n transport !== undefined && transport !== null && typeof transport === 'string'\n );\n\n // If no valid transports, default to 'internal' for platform authenticators\n const transports = validTransports.length > 0 ? validTransports : ['internal'];\n\n const clientAuth: ClientAuthenticatorData = {\n credentialId: auth.credentialId,\n credentialPublicKey: auth.credentialPublicKey,\n transports,\n name: auth.name,\n nearAccountId: toAccountId(nearAccountId),\n deviceNumber: auth.deviceNumber || 1, // Default to device 1 (1-indexed)\n registered: auth.registered,\n syncedAt: syncedAt,\n vrfPublicKey: auth.vrfPublicKey,\n };\n await this.storeAuthenticator(clientAuth);\n }\n }\n\n // === ATOMIC OPERATIONS AND ROLLBACK METHODS ===\n\n /**\n * Delete all authenticators for a user\n */\n async deleteAllAuthenticatorsForUser(nearAccountId: AccountId): Promise<void> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n\n if (authenticators.length === 0) {\n console.warn(`No authenticators found for user ${nearAccountId}`);\n return;\n }\n\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readwrite');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n\n for (const auth of authenticators) {\n // Composite PK is [nearAccountId, deviceNumber, credentialId]\n await store.delete([nearAccountId, auth.deviceNumber, auth.credentialId]);\n }\n\n console.debug(`Deleted ${authenticators.length} authenticators for user ${nearAccountId}`);\n }\n\n /**\n * Get user's confirmation config from IndexedDB\n * @param nearAccountId - The user's account ID\n * @returns ConfirmationConfig or undefined\n */\n async getConfirmationConfig(nearAccountId: AccountId): Promise<ConfirmationConfig> {\n const user = await this.getUser(nearAccountId);\n return user?.preferences?.confirmationConfig || DEFAULT_CONFIRMATION_CONFIG;\n }\n\n /**\n * Get user's theme preference from IndexedDB\n * @param nearAccountId - The user's account ID\n * @returns 'dark' | 'light' | null\n */\n async getTheme(nearAccountId: AccountId): Promise<'dark' | 'light' | null> {\n const user = await this.getUser(nearAccountId);\n return user?.preferences?.confirmationConfig.theme || null;\n }\n\n /**\n * Set user's theme preference in IndexedDB\n * @param nearAccountId - The user's account ID\n * @param theme - The theme to set ('dark' | 'light')\n */\n async setTheme(nearAccountId: AccountId, theme: 'dark' | 'light'): Promise<void> {\n const existingConfig = await this.getConfirmationConfig(nearAccountId);\n const confirmationConfig = { ...existingConfig, theme };\n await this.updatePreferences(nearAccountId, { confirmationConfig });\n }\n\n /**\n * Get user's theme with fallback to 'dark'\n * @param nearAccountId - The user's account ID\n * @returns 'dark' | 'light'\n */\n async getThemeOrDefault(nearAccountId: AccountId): Promise<'dark' | 'light'> {\n const theme = await this.getTheme(nearAccountId);\n return theme || 'dark';\n }\n\n /**\n * Get user's signer mode preference from IndexedDB\n */\n async getSignerMode(nearAccountId: AccountId): Promise<SignerMode> {\n const user = await this.getUser(nearAccountId);\n const raw = user?.preferences?.signerMode as SignerMode | SignerMode['mode'] | null | undefined;\n return coerceSignerMode(raw, DEFAULT_SIGNING_MODE);\n }\n\n /**\n * Set user's signer mode preference in IndexedDB\n */\n async setSignerMode(nearAccountId: AccountId, signerMode: SignerMode | SignerMode['mode']): Promise<void> {\n const next = coerceSignerMode(signerMode, DEFAULT_SIGNING_MODE);\n await this.updatePreferences(nearAccountId, { signerMode: next });\n }\n\n /**\n * Toggle between dark and light theme for a user\n * @param nearAccountId - The user's account ID\n * @returns The new theme that was set\n */\n async toggleTheme(nearAccountId: AccountId): Promise<'dark' | 'light'> {\n const currentTheme = await this.getThemeOrDefault(nearAccountId);\n const newTheme = currentTheme === 'dark' ? 'light' : 'dark';\n await this.setTheme(nearAccountId, newTheme);\n return newTheme;\n }\n\n // === DERIVED ADDRESS METHODS ===\n\n /**\n * Store a derived address for a given NEAR account + contract + path\n */\n async setDerivedAddress(nearAccountId: AccountId, args: { contractId: string; path: string; address: string }): Promise<void> {\n if (!nearAccountId || !args?.contractId || !args?.path || !args?.address) return;\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) return;\n const rec: DerivedAddressRecord = {\n nearAccountId: toAccountId(nearAccountId),\n contractId: String(args.contractId),\n path: String(args.path),\n address: String(args.address),\n updatedAt: Date.now(),\n };\n const db = await this.getDB();\n await db.put(DB_CONFIG.derivedAddressStore, rec);\n }\n\n /**\n * Fetch a derived address record; returns null if not found\n */\n async getDerivedAddressRecord(nearAccountId: AccountId, args: { contractId: string; path: string }): Promise<DerivedAddressRecord | null> {\n if (!nearAccountId || !args?.contractId || !args?.path) return null;\n const db = await this.getDB();\n const rec = await db.get(DB_CONFIG.derivedAddressStore, [toAccountId(nearAccountId), String(args.contractId), String(args.path)]);\n return (rec as DerivedAddressRecord) || null;\n }\n\n /**\n * Get only the derived address string; returns null if not set\n */\n async getDerivedAddress(nearAccountId: AccountId, args: { contractId: string; path: string }): Promise<string | null> {\n const rec = await this.getDerivedAddressRecord(nearAccountId, args);\n return rec?.address || null;\n }\n\n // === RECOVERY EMAIL METHODS ===\n\n /**\n * Upsert recovery email records for an account.\n * Merges by hashHex, preferring the most recent email.\n */\n async upsertRecoveryEmails(\n nearAccountId: AccountId,\n entries: Array<{ hashHex: string; email: string }>\n ): Promise<void> {\n if (!nearAccountId || !entries?.length) return;\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) return;\n\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const now = Date.now();\n\n for (const entry of entries) {\n const hashHex = String(entry?.hashHex || '').trim();\n const email = String(entry?.email || '').trim();\n if (!hashHex || !email) continue;\n\n const rec: RecoveryEmailRecord = {\n nearAccountId: accountId,\n hashHex,\n email,\n addedAt: now,\n };\n await db.put(DB_CONFIG.recoveryEmailStore, rec);\n }\n }\n\n /**\n * Fetch all recovery email records for an account.\n */\n async getRecoveryEmails(nearAccountId: AccountId): Promise<RecoveryEmailRecord[]> {\n if (!nearAccountId) return [];\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const tx = db.transaction(DB_CONFIG.recoveryEmailStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.recoveryEmailStore);\n const index = store.index('nearAccountId');\n const result = await index.getAll(accountId);\n return (result as RecoveryEmailRecord[]) || [];\n }\n\n /**\n * Atomic operation wrapper for multiple IndexedDB operations\n * Either all operations succeed or all are rolled back\n */\n async atomicOperation<T>(operation: (db: IDBPDatabase) => Promise<T>): Promise<T> {\n const db = await this.getDB();\n try {\n const result = await operation(db);\n return result;\n } catch (error) {\n console.error('Atomic operation failed:', error);\n throw error;\n }\n }\n\n /**\n * Complete rollback of user registration data\n * Deletes user, authenticators, and WebAuthn data atomically\n */\n async rollbackUserRegistration(nearAccountId: AccountId): Promise<void> {\n console.debug(`Rolling back registration data for ${nearAccountId}`);\n\n await this.atomicOperation(async (db) => {\n // Delete all authenticators for this user\n await this.deleteAllAuthenticatorsForUser(nearAccountId);\n\n // Delete user record\n await db.delete(DB_CONFIG.userStore, nearAccountId);\n\n // Clear from app state if this was the last user\n const lastUserAccount = await this.getAppState<string>('lastUserAccountId');\n if (lastUserAccount === nearAccountId) {\n await this.setAppState('lastUserAccountId', null);\n }\n\n console.debug(`Rolled back all registration data for ${nearAccountId}`);\n return true;\n });\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"passkeyClientDB.js","names":["DB_CONFIG: PasskeyClientDBConfig","err: any","entry: AppStateEntry<T>","validateNearAccountId","toAccountId","userData: ClientUserData","DEFAULT_CONFIRMATION_CONFIG","lastUserState: LastUserAccountIdState","fixed: ClientUserData","updatedUser: ClientUserData","clientAuth: ClientAuthenticatorData","coerceSignerMode","DEFAULT_SIGNING_MODE","rec: DerivedAddressRecord","rec: RecoveryEmailRecord"],"sources":["../../../../src/core/IndexedDBManager/passkeyClientDB.ts"],"sourcesContent":["import { openDB, type IDBPDatabase } from 'idb';\nimport { type ValidationResult, validateNearAccountId } from '../../utils/validation';\nimport type { AccountId } from '../types/accountIds';\nimport { toAccountId } from '../types/accountIds';\nimport {\n ConfirmationConfig,\n DEFAULT_CONFIRMATION_CONFIG,\n type SignerMode,\n DEFAULT_SIGNING_MODE,\n coerceSignerMode,\n} from '../types/signer-worker'\n\n\nexport interface ClientUserData {\n // Primary key - now uses AccountId + deviceNumber for unique identification\n nearAccountId: AccountId;\n deviceNumber: number; // Device number for multi-device support (1-indexed)\n version?: number;\n\n // User metadata\n registeredAt?: number;\n lastLogin?: number;\n lastUpdated?: number;\n\n // WebAuthn/Passkey data (merged from WebAuthnManager)\n clientNearPublicKey: string;\n passkeyCredential: {\n id: string;\n rawId: string;\n };\n\n // VRF credentials for stateless authentication\n encryptedVrfKeypair: {\n encryptedVrfDataB64u: string;\n chacha20NonceB64u: string;\n };\n // Server-assisted auto-login (VRF key session): Shamir 3-pass fields\n // Stores relayer-blinded KEK and the VRF ciphertext; server never sees plaintext VRF or KEK\n serverEncryptedVrfKeypair?: {\n ciphertextVrfB64u: string;\n kek_s_b64u: string;\n // Metadata for proactive refresh\n serverKeyId: string;\n updatedAt?: number;\n };\n\n // User preferences\n preferences?: UserPreferences;\n}\n\nexport type StoreUserDataInput = Omit<ClientUserData, 'deviceNumber' | 'lastLogin' | 'registeredAt'>\n & {\n deviceNumber?: number;\n serverEncryptedVrfKeypair?: ClientUserData['serverEncryptedVrfKeypair'];\n version?: number;\n };\n\nexport type StoreWebAuthnUserDataInput = {\n nearAccountId: AccountId;\n deviceNumber: number;\n clientNearPublicKey: string;\n lastUpdated?: number;\n version?: number;\n passkeyCredential: ClientUserData['passkeyCredential'];\n encryptedVrfKeypair: ClientUserData['encryptedVrfKeypair'];\n serverEncryptedVrfKeypair?: ClientUserData['serverEncryptedVrfKeypair'];\n};\n\nexport interface UserPreferences {\n useRelayer: boolean;\n useNetwork: 'testnet' | 'mainnet';\n confirmationConfig: ConfirmationConfig;\n signerMode?: SignerMode;\n // User preferences can be extended here as needed\n}\n\n// Authenticator cache\nexport interface ClientAuthenticatorData {\n credentialId: string;\n credentialPublicKey: Uint8Array;\n transports?: string[]; // AuthenticatorTransport[]\n name?: string;\n nearAccountId: AccountId; // FK reference using AccountId\n deviceNumber: number; // Device number for this authenticator (1-indexed)\n registered: string; // ISO date string\n syncedAt: string; // When this cache entry was last synced with contract\n vrfPublicKey: string; // Base64-encoded VRF public key (1:1 relationship on client)\n}\n\ninterface AppStateEntry<T = unknown> {\n key: string;\n value: T;\n}\n\n// Internal helper: legacy user records may be missing deviceNumber.\ntype ClientUserDataWithOptionalDevice =\n | ClientUserData\n | (Omit<ClientUserData, 'deviceNumber'> & { deviceNumber?: number });\n\n// Special type for lastUserAccountId app state entry\nexport interface LastUserAccountIdState {\n accountId: AccountId;\n deviceNumber: number;\n}\n\ninterface PasskeyClientDBConfig {\n dbName: string;\n dbVersion: number;\n userStore: string;\n appStateStore: string;\n authenticatorStore: string;\n derivedAddressStore: string;\n recoveryEmailStore: string;\n}\n\n// === CONSTANTS ===\nconst DB_CONFIG: PasskeyClientDBConfig = {\n dbName: 'PasskeyClientDB',\n dbVersion: 15, // v15: add recoveryEmails store\n userStore: 'users',\n appStateStore: 'appState',\n authenticatorStore: 'authenticators',\n derivedAddressStore: 'derivedAddresses',\n recoveryEmailStore: 'recoveryEmails'\n} as const;\n\nexport interface IndexedDBEvent {\n type: 'user-updated' | 'preferences-updated' | 'user-deleted';\n accountId: AccountId;\n data?: Record<string, unknown>;\n}\n\n// Persisted mapping of derived (e.g., EVM) addresses tied to an account\n/**\n * Persisted mapping of derived (e.g., EVM/Solana/Zcash) addresses tied to an account.\n *\n * Notes on multi-chain support:\n * - The composite primary key is [nearAccountId, contractId, path]. To support\n * different chains and chain IDs, encode them in the `path` string, e.g.:\n * - EVM: `evm:<chainId>:<derivationPath>` → `evm:84532:ethereum-1`\n * - Solana: `solana:<derivationPath>`\n * - Zcash: `zcash:<derivationPath>`\n * - Additional descriptive fields like `namespace` and `chainRef` are optional metadata\n * and are not part of the key.\n */\nexport interface DerivedAddressRecord {\n nearAccountId: AccountId;\n contractId: string; // MPC/Derivation contract on NEAR\n path: string; // Composite path (may include namespace/chainId); see docs above\n address: string; // Derived address (e.g., 0x...)\n updatedAt: number;\n // Optional metadata (not used in the key)\n namespace?: string; // e.g., 'evm', 'solana', 'zcash'\n chainRef?: string; // e.g., chainId '84532' or a named network slug\n}\n\n/**\n * Persisted mapping of recovery email hashes to canonical email addresses for an account.\n *\n * Notes:\n * - Composite primary key is [nearAccountId, hashHex].\n * - `hashHex` is the 0x-prefixed hex encoding of the 32-byte hash:\n * SHA256(canonical_email || \"|\" || account_id)\n * - `email` is the canonical form: \"local@domain\", lowercased.\n */\nexport interface RecoveryEmailRecord {\n nearAccountId: AccountId;\n hashHex: string;\n email: string;\n addedAt: number;\n}\n\nexport class PasskeyClientDBManager {\n private config: PasskeyClientDBConfig;\n private db: IDBPDatabase | null = null;\n private disabled = false;\n private eventListeners: Set<(event: IndexedDBEvent) => void> = new Set();\n\n constructor(config: PasskeyClientDBConfig = DB_CONFIG) {\n this.config = config;\n }\n\n getDbName(): string {\n return this.config.dbName;\n }\n\n setDbName(dbName: string): void {\n const next = String(dbName || '').trim();\n if (!next || next === this.config.dbName) return;\n try { (this.db as any)?.close?.(); } catch {}\n this.db = null;\n this.config = { ...this.config, dbName: next };\n }\n\n isDisabled(): boolean {\n return this.disabled;\n }\n\n setDisabled(disabled: boolean): void {\n const next = !!disabled;\n if (next === this.disabled) return;\n this.disabled = next;\n if (next) {\n try { (this.db as any)?.close?.(); } catch {}\n this.db = null;\n }\n }\n\n // === EVENT SYSTEM ===\n\n onChange(listener: (event: IndexedDBEvent) => void): () => void {\n this.eventListeners.add(listener);\n return () => {\n this.eventListeners.delete(listener);\n };\n }\n\n private emitEvent(event: IndexedDBEvent): void {\n this.eventListeners.forEach(listener => {\n try {\n listener(event);\n } catch (error) {\n console.warn('[IndexedDBManager]: Error in event listener:', error);\n }\n });\n }\n\n private async getDB(): Promise<IDBPDatabase> {\n if (this.disabled) {\n throw new Error('[PasskeyClientDBManager] IndexedDB is disabled in this environment.');\n }\n if (this.db) {\n return this.db;\n }\n\n try {\n this.db = await openDB(this.config.dbName, this.config.dbVersion, {\n upgrade: (db, oldVersion, _newVersion, _transaction): void => {\n // Create stores if they don't exist\n if (!db.objectStoreNames.contains(DB_CONFIG.userStore)) {\n // Users table: composite key of [nearAccountId, deviceNumber]\n const userStore = db.createObjectStore(DB_CONFIG.userStore, { keyPath: ['nearAccountId', 'deviceNumber'] });\n userStore.createIndex('nearAccountId', 'nearAccountId', { unique: false });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.appStateStore)) {\n db.createObjectStore(DB_CONFIG.appStateStore, { keyPath: 'key' });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.authenticatorStore)) {\n // Authenticators table: composite key of [nearAccountId, deviceNumber, credentialId]\n const authStore = db.createObjectStore(DB_CONFIG.authenticatorStore, { keyPath: ['nearAccountId', 'deviceNumber', 'credentialId'] });\n authStore.createIndex('nearAccountId', 'nearAccountId', { unique: false });\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.derivedAddressStore)) {\n // Derived addresses: composite key of [nearAccountId, contractId, path]\n const dStore = db.createObjectStore(DB_CONFIG.derivedAddressStore, { keyPath: ['nearAccountId', 'contractId', 'path'] });\n try { dStore.createIndex('nearAccountId', 'nearAccountId', { unique: false }); } catch {}\n }\n if (!db.objectStoreNames.contains(DB_CONFIG.recoveryEmailStore)) {\n // Recovery emails: composite key of [nearAccountId, hashHex]\n const rStore = db.createObjectStore(DB_CONFIG.recoveryEmailStore, { keyPath: ['nearAccountId', 'hashHex'] });\n try { rStore.createIndex('nearAccountId', 'nearAccountId', { unique: false }); } catch {}\n }\n },\n blocked() {\n console.warn('PasskeyClientDB connection is blocked.');\n },\n blocking() {\n console.warn('PasskeyClientDB connection is blocking another connection.');\n },\n terminated: () => {\n console.warn('PasskeyClientDB connection has been terminated.');\n this.db = null;\n },\n });\n\n // Post-open migrations (non-blocking)\n try { await this.runMigrationsIfNeeded(this.db); } catch {}\n\n } catch (err: any) {\n const msg = String(err?.message || '');\n if (err?.name === 'VersionError' || /less than the existing version/i.test(msg)) {\n // Mixed-version contexts (host/app) — open without version to adopt existing DB\n try {\n console.warn('PasskeyClientDB: opening existing DB without version due to VersionError');\n this.db = await openDB(this.config.dbName);\n } catch (e) {\n throw err;\n }\n } else {\n throw err;\n }\n }\n\n return this.db;\n }\n\n private async runMigrationsIfNeeded(_db: IDBPDatabase): Promise<void> {\n return;\n }\n\n // === APP STATE METHODS ===\n\n async getAppState<T = unknown>(key: string): Promise<T | undefined> {\n const db = await this.getDB();\n const result = await db.get(DB_CONFIG.appStateStore, key);\n return result?.value as T | undefined;\n }\n\n async setAppState<T = unknown>(key: string, value: T): Promise<void> {\n const db = await this.getDB();\n const entry: AppStateEntry<T> = { key, value };\n await db.put(DB_CONFIG.appStateStore, entry);\n }\n\n // === ACCOUNT ID VALIDATION AND UTILITIES ===\n\n /**\n * Validate that a NEAR account ID is in the expected format\n * Supports both <username>.<relayerAccountId> and <username>.testnet formats\n */\n validateNearAccountId(nearAccountId: AccountId): ValidationResult {\n return validateNearAccountId(nearAccountId);\n }\n\n /**\n * Extract username from NEAR account ID\n */\n extractUsername(nearAccountId: AccountId): string {\n const validation = validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n throw new Error(`Invalid NEAR account ID: ${validation.error}`);\n }\n return nearAccountId.split('.')[0];\n }\n\n /**\n * Generate a NEAR account ID from a username and domain\n * @param username - The username to use for the account ID\n * @param domain - The domain to use for the account ID\n * @returns The generated NEAR account ID\n */\n generateNearAccountId(username: string, domain: string): string {\n const sanitizedName = username\n .toLowerCase()\n .replace(/[^a-z0-9_\\\\-]/g, '')\n .substring(0, 32);\n return `${sanitizedName}.${domain}`;\n }\n\n // === USER MANAGEMENT METHODS ===\n\n async getUser(nearAccountId: AccountId, deviceNumber?: number): Promise<ClientUserData | null> {\n if (!nearAccountId) return null;\n\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) {\n console.warn(`Invalid account ID format: ${nearAccountId}`);\n return null;\n }\n\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n\n if (typeof deviceNumber === 'number') {\n const rec = await db.get(DB_CONFIG.userStore, [accountId, deviceNumber]);\n if (!rec) return null;\n return await this.normalizeUserDeviceNumber(rec as ClientUserDataWithOptionalDevice, deviceNumber);\n }\n\n const index = db.transaction(DB_CONFIG.userStore).store.index('nearAccountId');\n const results = await index.getAll(accountId);\n if (results.length === 0) {\n return null;\n }\n\n if (results.length > 1) {\n console.warn(\n `Multiple passkeys found for account ${accountId}, deviceNumber not provided; ` +\n 'defaulting to last logged-in user.'\n );\n console.log('defaulting to last used user deviceNumber');\n const lastUserState = await this.getAppState<LastUserAccountIdState>('lastUserAccountId').catch(() => null);\n if (lastUserState && toAccountId(lastUserState.accountId) === accountId) {\n const keyed = await db.get(DB_CONFIG.userStore, [accountId, lastUserState.deviceNumber]);\n if (keyed) {\n return await this.normalizeUserDeviceNumber(\n keyed as ClientUserDataWithOptionalDevice,\n lastUserState.deviceNumber\n );\n }\n }\n }\n\n const first = results[0] as ClientUserDataWithOptionalDevice;\n if (!first) return null;\n return await this.normalizeUserDeviceNumber(first, 1);\n }\n\n /**\n * Get the current/last user\n * This is maintained via app state and updated whenever a user is stored or updated\n */\n async getLastUser(): Promise<ClientUserData | null> {\n const lastUserState = await this.getAppState<LastUserAccountIdState>('lastUserAccountId');\n if (!lastUserState) return null;\n const db = await this.getDB();\n const accountId = toAccountId(lastUserState.accountId);\n // Prefer exact device match using composite primary key\n const record = await db.get(DB_CONFIG.userStore, [accountId, lastUserState.deviceNumber]);\n if (record) return record as ClientUserData;\n // Fallback: return any user for account\n return this.getUser(accountId);\n }\n\n /** Get user record by composite key (nearAccountId, deviceNumber) */\n async getUserByDevice(nearAccountId: AccountId, deviceNumber: number): Promise<ClientUserData | null> {\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const rec = await db.get(DB_CONFIG.userStore, [accountId, deviceNumber]);\n return rec as ClientUserData || null;\n }\n\n /**\n * Get the most recently updated user record for a given account.\n * Useful when deviceNumber is unknown but we need the freshest key for the account.\n */\n /**\n * Get the most recently updated user record for a given account.\n * Useful when deviceNumber is unknown but we need the freshest key for the account.\n */\n async getLastDBUpdatedUser(nearAccountId: AccountId): Promise<ClientUserData | null> {\n const db = await this.getDB();\n try {\n const idx = db.transaction(DB_CONFIG.userStore).store.index('nearAccountId');\n const all = await idx.getAll(toAccountId(nearAccountId));\n if (Array.isArray(all) && all.length > 0) {\n const latest = (all as ClientUserData[]).reduce((a, b) =>\n (a.lastUpdated ?? 0) >= (b.lastUpdated ?? 0) ? a : b\n );\n return latest;\n }\n } catch {\n // fall through\n }\n return null;\n }\n\n async hasPasskeyCredential(nearAccountId: AccountId): Promise<boolean> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n return !!authenticators[0]?.credentialId;\n }\n\n /**\n * Ensure the current passkey selection is aligned with the last logged-in device.\n *\n * - When multiple authenticators exist for an account and no deviceNumber is specified,\n * this helper prefers authenticators whose deviceNumber matches the last logged-in user.\n * - Optionally validates that a selected credential (by rawId) also matches the last-user device.\n *\n * @param nearAccountId - Account ID for which the operation is being performed\n * @param authenticators - All authenticators stored for the account\n * @param selectedCredentialRawId - Optional rawId of the credential chosen by WebAuthn\n * @returns filtered authenticators for allowCredentials, plus optional wrongPasskeyError\n */\n async ensureCurrentPasskey(\n nearAccountId: AccountId,\n authenticators: ClientAuthenticatorData[],\n selectedCredentialRawId?: string,\n ): Promise<{\n authenticatorsForPrompt: ClientAuthenticatorData[];\n wrongPasskeyError?: string;\n }> {\n if (authenticators.length <= 1) {\n return { authenticatorsForPrompt: authenticators };\n }\n\n const accountIdNormalized = toAccountId(nearAccountId);\n const lastUser = await this.getLastUser().catch(() => null);\n if (!lastUser || lastUser.nearAccountId !== accountIdNormalized) {\n return { authenticatorsForPrompt: authenticators };\n }\n\n const expectedDeviceNumber = lastUser.deviceNumber;\n const byDeviceNumber = authenticators.filter(a => a.deviceNumber === expectedDeviceNumber);\n\n // Prefer the credentialId for the last-user deviceNumber; use the stored last-user rawId\n // only when it matches an authenticator for that device (or when we have no device match).\n let expectedCredentialId = lastUser.passkeyCredential.rawId;\n if (byDeviceNumber.length > 0 && !byDeviceNumber.some(a => a.credentialId === expectedCredentialId)) {\n expectedCredentialId = byDeviceNumber[0].credentialId;\n }\n\n // Preference: restrict allowCredentials to the last-user credentialId.\n // Fallback: if the local authenticator cache is missing that entry, prefer the last-user deviceNumber.\n const byCredentialId = authenticators.filter(a => a.credentialId === expectedCredentialId);\n const authenticatorsForPrompt =\n byCredentialId.length > 0\n ? byCredentialId\n : (byDeviceNumber.length > 0 ? byDeviceNumber : authenticators);\n\n const wrongPasskeyError =\n selectedCredentialRawId && selectedCredentialRawId !== expectedCredentialId\n ? (\n `You have multiple passkeys (deviceNumbers) for account ${accountIdNormalized}, ` +\n 'but used a different passkey than the most recently logged-in one. Please use the passkey for the most recently logged-in device.'\n )\n : undefined;\n\n return { authenticatorsForPrompt, wrongPasskeyError };\n }\n\n /**\n * Register a new user with the given NEAR account ID\n * @param nearAccountId - Full NEAR account ID (e.g., \"username.testnet\" or \"username.relayer.testnet\")\n * @param additionalData - Additional user data to store\n */\n async registerUser(storeUserData: StoreUserDataInput): Promise<ClientUserData> {\n\n const validation = this.validateNearAccountId(storeUserData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot register user with invalid account ID: ${validation.error}`);\n }\n\n const now = Date.now();\n\n const userData: ClientUserData = {\n nearAccountId: toAccountId(storeUserData.nearAccountId),\n deviceNumber: storeUserData.deviceNumber || 1, // Default to device 1 (1-indexed)\n version: storeUserData.version || 2,\n registeredAt: now,\n lastLogin: now,\n lastUpdated: now,\n clientNearPublicKey: storeUserData.clientNearPublicKey,\n passkeyCredential: storeUserData.passkeyCredential,\n preferences: {\n useRelayer: false,\n useNetwork: 'testnet',\n confirmationConfig: DEFAULT_CONFIRMATION_CONFIG,\n // Default preferences can be set here\n },\n encryptedVrfKeypair: storeUserData.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: storeUserData.serverEncryptedVrfKeypair,\n };\n\n await this.storeUser(userData);\n return userData;\n }\n\n async updateUser(nearAccountId: AccountId, updates: Partial<ClientUserData>, deviceNumber?: number): Promise<void> {\n const user = await this.getUser(nearAccountId, deviceNumber);\n if (user) {\n const updatedUser = {\n ...user,\n ...updates,\n lastUpdated: Date.now()\n };\n await this.storeUser(updatedUser); // This will update the app state lastUserAccountId\n\n // Emit event for user updates\n this.emitEvent({\n type: 'user-updated',\n accountId: nearAccountId,\n data: { updates, updatedUser }\n });\n }\n }\n\n async updateLastLogin(nearAccountId: AccountId): Promise<void> {\n await this.updateUser(nearAccountId, { lastLogin: Date.now() });\n }\n\n /**\n * Set the last logged-in user\n * @param nearAccountId - The account ID of the user\n * @param deviceNumber - The device number (defaults to 1)\n */\n async setLastUser(nearAccountId: AccountId, deviceNumber: number = 1): Promise<void> {\n const lastUserState: LastUserAccountIdState = {\n accountId: nearAccountId,\n deviceNumber,\n };\n await this.setAppState('lastUserAccountId', lastUserState);\n }\n\n async updatePreferences(\n nearAccountId: AccountId,\n preferences: Partial<UserPreferences>\n ): Promise<void> {\n const user = await this.getUser(nearAccountId);\n if (user) {\n const updatedPreferences = {\n ...user.preferences,\n ...preferences\n } as UserPreferences;\n await this.updateUser(nearAccountId, { preferences: updatedPreferences });\n\n // Emit event for preference changes\n this.emitEvent({\n type: 'preferences-updated',\n accountId: nearAccountId,\n data: { preferences: updatedPreferences }\n });\n }\n }\n\n private async normalizeUserDeviceNumber(\n user: ClientUserDataWithOptionalDevice,\n defaultDeviceNumber: number\n ): Promise<ClientUserData> {\n const hasValidDevice =\n typeof user.deviceNumber === 'number' && Number.isFinite(user.deviceNumber);\n if (hasValidDevice) {\n return user as ClientUserData;\n }\n\n const deviceNumber = defaultDeviceNumber;\n const fixed: ClientUserData = {\n ...(user as Omit<ClientUserData, 'deviceNumber'>),\n deviceNumber,\n };\n await this.storeUser(fixed);\n return fixed;\n }\n\n private async storeUser(userData: ClientUserData): Promise<void> {\n const validation = this.validateNearAccountId(userData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot store user with invalid account ID: ${validation.error}`);\n }\n\n const db = await this.getDB();\n await db.put(DB_CONFIG.userStore, userData);\n\n // Update lastUserAccountId with new format including device info\n const lastUserState: LastUserAccountIdState = {\n accountId: userData.nearAccountId,\n deviceNumber: userData.deviceNumber,\n };\n\n await this.setAppState('lastUserAccountId', lastUserState);\n }\n\n /**\n * Store WebAuthn user data (compatibility with WebAuthnManager)\n * @param userData - User data with nearAccountId as primary identifier\n */\n async storeWebAuthnUserData(userData: StoreWebAuthnUserDataInput): Promise<void> {\n const validation = this.validateNearAccountId(userData.nearAccountId);\n if (!validation.valid) {\n throw new Error(`Cannot store WebAuthn data for invalid account ID: ${validation.error}`);\n }\n\n const accountId = toAccountId(userData.nearAccountId);\n const deviceNumber = userData.deviceNumber;\n let user = await this.getUser(accountId, deviceNumber);\n\n if (!user) {\n user = await this.registerUser({\n nearAccountId: accountId,\n deviceNumber,\n clientNearPublicKey: userData.clientNearPublicKey,\n passkeyCredential: userData.passkeyCredential,\n encryptedVrfKeypair: userData.encryptedVrfKeypair,\n version: userData.version || 2,\n serverEncryptedVrfKeypair: userData.serverEncryptedVrfKeypair,\n });\n }\n\n const updatedUser: ClientUserData = {\n ...user,\n clientNearPublicKey: userData.clientNearPublicKey,\n passkeyCredential: userData.passkeyCredential,\n encryptedVrfKeypair: userData.encryptedVrfKeypair,\n serverEncryptedVrfKeypair: userData.serverEncryptedVrfKeypair ?? user.serverEncryptedVrfKeypair,\n version: userData.version ?? user.version,\n lastUpdated: userData.lastUpdated ?? Date.now(),\n };\n\n await this.storeUser(updatedUser);\n this.emitEvent({\n type: 'user-updated',\n accountId,\n data: { updatedUser }\n });\n }\n\n async getAllUsers(): Promise<ClientUserData[]> {\n const db = await this.getDB();\n return db.getAll(DB_CONFIG.userStore);\n }\n\n async deleteUser(nearAccountId: AccountId): Promise<void> {\n const db = await this.getDB();\n await db.delete(DB_CONFIG.userStore, nearAccountId);\n // Also clean up related authenticators\n await this.clearAuthenticatorsForUser(nearAccountId);\n }\n\n async clearAllUsers(): Promise<void> {\n const db = await this.getDB();\n await db.clear(DB_CONFIG.userStore);\n }\n\n async clearAllAppState(): Promise<void> {\n const db = await this.getDB();\n await db.clear(DB_CONFIG.appStateStore);\n }\n\n /**\n * Store authenticator data for a user\n */\n async storeAuthenticator(authenticatorData: ClientAuthenticatorData): Promise<void> {\n const db = await this.getDB();\n await db.put(DB_CONFIG.authenticatorStore, authenticatorData);\n }\n\n /**\n * Get all authenticators for a user (optionally for a specific device)\n */\n async getAuthenticatorsByUser(nearAccountId: AccountId): Promise<ClientAuthenticatorData[]> {\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n const accountId = toAccountId(nearAccountId);\n\n // Get all authenticators for this account across all devices\n const index = store.index('nearAccountId');\n return await index.getAll(accountId);\n }\n\n /**\n * Get a specific authenticator by credential ID\n */\n async getAuthenticatorByCredentialId(\n nearAccountId: AccountId,\n credentialId: string\n ): Promise<ClientAuthenticatorData | null> {\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n const accountId = toAccountId(nearAccountId);\n\n // Primary key is [nearAccountId, deviceNumber, credentialId], so we cannot\n // look up by [nearAccountId, credentialId] directly. Use the nearAccountId\n // index and filter by credentialId.\n const index = store.index('nearAccountId');\n const all = await index.getAll(accountId);\n const match = all.find((auth: any) => auth.credentialId === credentialId) || null;\n return match;\n }\n\n /**\n * Clear all authenticators for a user\n */\n async clearAuthenticatorsForUser(nearAccountId: AccountId): Promise<void> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readwrite');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n\n for (const auth of authenticators) {\n // Composite PK is [nearAccountId, deviceNumber, credentialId]\n await store.delete([nearAccountId, auth.deviceNumber, auth.credentialId]);\n }\n }\n\n /**\n * Sync authenticators from contract data\n */\n async syncAuthenticatorsFromContract(\n nearAccountId: AccountId,\n contractAuthenticators: Array<{\n credentialId: string;\n credentialPublicKey: Uint8Array;\n transports?: string[];\n name?: string;\n registered: string;\n vrfPublicKey: string;\n deviceNumber?: number; // Device number from contract\n }>\n ): Promise<void> {\n // Clear existing cache for this user\n await this.clearAuthenticatorsForUser(nearAccountId);\n\n // Add all contract authenticators to cache\n const syncedAt = new Date().toISOString();\n for (const auth of contractAuthenticators) {\n // Fix transport processing: filter out undefined values and provide fallback\n const rawTransports = auth.transports || [];\n const validTransports = rawTransports.filter((transport: any) =>\n transport !== undefined && transport !== null && typeof transport === 'string'\n );\n\n // If no valid transports, default to 'internal' for platform authenticators\n const transports = validTransports.length > 0 ? validTransports : ['internal'];\n\n const clientAuth: ClientAuthenticatorData = {\n credentialId: auth.credentialId,\n credentialPublicKey: auth.credentialPublicKey,\n transports,\n name: auth.name,\n nearAccountId: toAccountId(nearAccountId),\n deviceNumber: auth.deviceNumber || 1, // Default to device 1 (1-indexed)\n registered: auth.registered,\n syncedAt: syncedAt,\n vrfPublicKey: auth.vrfPublicKey,\n };\n await this.storeAuthenticator(clientAuth);\n }\n }\n\n // === ATOMIC OPERATIONS AND ROLLBACK METHODS ===\n\n /**\n * Delete all authenticators for a user\n */\n async deleteAllAuthenticatorsForUser(nearAccountId: AccountId): Promise<void> {\n const authenticators = await this.getAuthenticatorsByUser(nearAccountId);\n\n if (authenticators.length === 0) {\n console.warn(`No authenticators found for user ${nearAccountId}`);\n return;\n }\n\n const db = await this.getDB();\n const tx = db.transaction(DB_CONFIG.authenticatorStore, 'readwrite');\n const store = tx.objectStore(DB_CONFIG.authenticatorStore);\n\n for (const auth of authenticators) {\n // Composite PK is [nearAccountId, deviceNumber, credentialId]\n await store.delete([nearAccountId, auth.deviceNumber, auth.credentialId]);\n }\n\n console.debug(`Deleted ${authenticators.length} authenticators for user ${nearAccountId}`);\n }\n\n /**\n * Get user's confirmation config from IndexedDB\n * @param nearAccountId - The user's account ID\n * @returns ConfirmationConfig or undefined\n */\n async getConfirmationConfig(nearAccountId: AccountId): Promise<ConfirmationConfig> {\n const user = await this.getUser(nearAccountId);\n return user?.preferences?.confirmationConfig || DEFAULT_CONFIRMATION_CONFIG;\n }\n\n /**\n * Get user's theme preference from IndexedDB\n * @param nearAccountId - The user's account ID\n * @returns 'dark' | 'light' | null\n */\n async getTheme(nearAccountId: AccountId): Promise<'dark' | 'light' | null> {\n const user = await this.getUser(nearAccountId);\n return user?.preferences?.confirmationConfig.theme || null;\n }\n\n /**\n * Set user's theme preference in IndexedDB\n * @param nearAccountId - The user's account ID\n * @param theme - The theme to set ('dark' | 'light')\n */\n async setTheme(nearAccountId: AccountId, theme: 'dark' | 'light'): Promise<void> {\n const existingConfig = await this.getConfirmationConfig(nearAccountId);\n const confirmationConfig = { ...existingConfig, theme };\n await this.updatePreferences(nearAccountId, { confirmationConfig });\n }\n\n /**\n * Get user's theme with fallback to 'dark'\n * @param nearAccountId - The user's account ID\n * @returns 'dark' | 'light'\n */\n async getThemeOrDefault(nearAccountId: AccountId): Promise<'dark' | 'light'> {\n const theme = await this.getTheme(nearAccountId);\n return theme || 'dark';\n }\n\n /**\n * Get user's signer mode preference from IndexedDB\n */\n async getSignerMode(nearAccountId: AccountId): Promise<SignerMode> {\n const user = await this.getUser(nearAccountId);\n const raw = user?.preferences?.signerMode as SignerMode | SignerMode['mode'] | null | undefined;\n return coerceSignerMode(raw, DEFAULT_SIGNING_MODE);\n }\n\n /**\n * Set user's signer mode preference in IndexedDB\n */\n async setSignerMode(nearAccountId: AccountId, signerMode: SignerMode | SignerMode['mode']): Promise<void> {\n const next = coerceSignerMode(signerMode, DEFAULT_SIGNING_MODE);\n await this.updatePreferences(nearAccountId, { signerMode: next });\n }\n\n /**\n * Toggle between dark and light theme for a user\n * @param nearAccountId - The user's account ID\n * @returns The new theme that was set\n */\n async toggleTheme(nearAccountId: AccountId): Promise<'dark' | 'light'> {\n const currentTheme = await this.getThemeOrDefault(nearAccountId);\n const newTheme = currentTheme === 'dark' ? 'light' : 'dark';\n await this.setTheme(nearAccountId, newTheme);\n return newTheme;\n }\n\n // === DERIVED ADDRESS METHODS ===\n\n /**\n * Store a derived address for a given NEAR account + contract + path\n */\n async setDerivedAddress(nearAccountId: AccountId, args: { contractId: string; path: string; address: string }): Promise<void> {\n if (!nearAccountId || !args?.contractId || !args?.path || !args?.address) return;\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) return;\n const rec: DerivedAddressRecord = {\n nearAccountId: toAccountId(nearAccountId),\n contractId: String(args.contractId),\n path: String(args.path),\n address: String(args.address),\n updatedAt: Date.now(),\n };\n const db = await this.getDB();\n await db.put(DB_CONFIG.derivedAddressStore, rec);\n }\n\n /**\n * Fetch a derived address record; returns null if not found\n */\n async getDerivedAddressRecord(nearAccountId: AccountId, args: { contractId: string; path: string }): Promise<DerivedAddressRecord | null> {\n if (!nearAccountId || !args?.contractId || !args?.path) return null;\n const db = await this.getDB();\n const rec = await db.get(DB_CONFIG.derivedAddressStore, [toAccountId(nearAccountId), String(args.contractId), String(args.path)]);\n return (rec as DerivedAddressRecord) || null;\n }\n\n /**\n * Get only the derived address string; returns null if not set\n */\n async getDerivedAddress(nearAccountId: AccountId, args: { contractId: string; path: string }): Promise<string | null> {\n const rec = await this.getDerivedAddressRecord(nearAccountId, args);\n return rec?.address || null;\n }\n\n // === RECOVERY EMAIL METHODS ===\n\n /**\n * Upsert recovery email records for an account.\n * Merges by hashHex, preferring the most recent email.\n */\n async upsertRecoveryEmails(\n nearAccountId: AccountId,\n entries: Array<{ hashHex: string; email: string }>\n ): Promise<void> {\n if (!nearAccountId || !entries?.length) return;\n const validation = this.validateNearAccountId(nearAccountId);\n if (!validation.valid) return;\n\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const now = Date.now();\n\n for (const entry of entries) {\n const hashHex = String(entry?.hashHex || '').trim();\n const email = String(entry?.email || '').trim();\n if (!hashHex || !email) continue;\n\n const rec: RecoveryEmailRecord = {\n nearAccountId: accountId,\n hashHex,\n email,\n addedAt: now,\n };\n await db.put(DB_CONFIG.recoveryEmailStore, rec);\n }\n }\n\n /**\n * Fetch all recovery email records for an account.\n */\n async getRecoveryEmails(nearAccountId: AccountId): Promise<RecoveryEmailRecord[]> {\n if (!nearAccountId) return [];\n const db = await this.getDB();\n const accountId = toAccountId(nearAccountId);\n const tx = db.transaction(DB_CONFIG.recoveryEmailStore, 'readonly');\n const store = tx.objectStore(DB_CONFIG.recoveryEmailStore);\n const index = store.index('nearAccountId');\n const result = await index.getAll(accountId);\n return (result as RecoveryEmailRecord[]) || [];\n }\n\n /**\n * Atomic operation wrapper for multiple IndexedDB operations\n * Either all operations succeed or all are rolled back\n */\n async atomicOperation<T>(operation: (db: IDBPDatabase) => Promise<T>): Promise<T> {\n const db = await this.getDB();\n try {\n const result = await operation(db);\n return result;\n } catch (error) {\n console.error('Atomic operation failed:', error);\n throw error;\n }\n }\n\n /**\n * Complete rollback of user registration data\n * Deletes user, authenticators, and WebAuthn data atomically\n */\n async rollbackUserRegistration(nearAccountId: AccountId): Promise<void> {\n console.debug(`Rolling back registration data for ${nearAccountId}`);\n\n await this.atomicOperation(async (db) => {\n // Delete all authenticators for this user\n await this.deleteAllAuthenticatorsForUser(nearAccountId);\n\n // Delete user record\n await db.delete(DB_CONFIG.userStore, nearAccountId);\n\n // Clear from app state if this was the last user\n const lastUserAccount = await this.getAppState<string>('lastUserAccountId');\n if (lastUserAccount === nearAccountId) {\n await this.setAppState('lastUserAccountId', null);\n }\n\n console.debug(`Rolled back all registration data for ${nearAccountId}`);\n return true;\n });\n }\n}\n"],"mappings":"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"}
|
|
@@ -30,23 +30,24 @@ async function createAccountAndRegisterWithRelayServer(context, nearAccountId, p
|
|
|
30
30
|
const serialized = isSerialized ? require_credentialsHelpers.normalizeRegistrationCredential(credential) : require_credentialsHelpers.serializeRegistrationCredential(credential);
|
|
31
31
|
const serializedCredential = require_credentialsHelpers.removePrfOutputGuard(serialized);
|
|
32
32
|
if (!Array.isArray(serializedCredential?.response?.transports)) serializedCredential.response.transports = [];
|
|
33
|
-
const
|
|
34
|
-
|
|
33
|
+
const intentDigestB64u = String(vrfChallenge.intentDigest || "").trim();
|
|
34
|
+
const intentDigestBytes = require_base64.base64UrlDecode(intentDigestB64u);
|
|
35
|
+
if (intentDigestBytes.length !== 32) throw new Error("Missing or invalid vrfChallenge.intentDigest (expected base64url-encoded 32 bytes)");
|
|
35
36
|
const requestData = {
|
|
36
37
|
new_account_id: nearAccountId,
|
|
37
38
|
new_public_key: publicKey,
|
|
38
39
|
device_number: 1,
|
|
39
40
|
...opts?.thresholdEd25519?.clientVerifyingShareB64u ? { threshold_ed25519: { client_verifying_share_b64u: opts.thresholdEd25519.clientVerifyingShareB64u } } : {},
|
|
40
41
|
vrf_data: {
|
|
41
|
-
vrf_input_data:
|
|
42
|
-
vrf_output:
|
|
43
|
-
vrf_proof:
|
|
44
|
-
public_key:
|
|
42
|
+
vrf_input_data: vrfChallenge.vrfInput,
|
|
43
|
+
vrf_output: vrfChallenge.vrfOutput,
|
|
44
|
+
vrf_proof: vrfChallenge.vrfProof,
|
|
45
|
+
public_key: vrfChallenge.vrfPublicKey,
|
|
45
46
|
user_id: vrfChallenge.userId,
|
|
46
47
|
rp_id: vrfChallenge.rpId,
|
|
47
48
|
block_height: Number(vrfChallenge.blockHeight),
|
|
48
|
-
block_hash:
|
|
49
|
-
intent_digest_32
|
|
49
|
+
block_hash: vrfChallenge.blockHash,
|
|
50
|
+
intent_digest_32: intentDigestB64u
|
|
50
51
|
},
|
|
51
52
|
webauthn_registration: serializedCredential,
|
|
52
53
|
deterministic_vrf_public_key: Array.from(require_base64.base64UrlDecode(deterministicVrfPublicKey)),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createAccountRelayServer.js","names":["isObject","RegistrationPhase","RegistrationStatus","serialized: WebAuthnRegistrationCredential","normalizeRegistrationCredential","serializeRegistrationCredential","removePrfOutputGuard","base64UrlDecode","requestData: CreateAccountAndRegisterUserRequest","result: CreateAccountAndRegisterResult","errorMessage","error: unknown"],"sources":["../../../../../src/core/TatchiPasskey/faucets/createAccountRelayServer.ts"],"sourcesContent":["import { VRFChallenge } from '../../types/vrf-worker';\nimport { RegistrationSSEEvent, RegistrationPhase, RegistrationStatus } from '../../types/sdkSentEvents';\nimport { PasskeyManagerContext } from '..';\nimport { base64UrlDecode } from '../../../utils/encoders';\nimport { removePrfOutputGuard, serializeRegistrationCredential, normalizeRegistrationCredential } from '../../WebAuthnManager/credentialsHelpers';\nimport type { WebAuthnRegistrationCredential } from '../../types/webauthn';\nimport type { AuthenticatorOptions } from '../../types/authenticatorOptions';\nimport type { CreateAccountAndRegisterResult } from '../../../server/core/types';\nimport { isObject } from '@/utils/validation';\nimport { errorMessage } from '../../../utils/errors';\n\nfunction isSerializedRegistrationCredential(\n credential: WebAuthnRegistrationCredential | PublicKeyCredential,\n): credential is WebAuthnRegistrationCredential {\n if (!isObject(credential)) return false;\n const resp = (credential as { response?: unknown }).response;\n if (!isObject(resp)) return false;\n return typeof (resp as { attestationObject?: unknown }).attestationObject === 'string';\n}\n\n/**\n * HTTP Request body for the relay server's /create_account_and_register_user endpoint\n */\nexport interface CreateAccountAndRegisterUserRequest {\n new_account_id: string;\n new_public_key: string;\n device_number: number;\n threshold_ed25519?: {\n client_verifying_share_b64u: string;\n };\n vrf_data: {\n vrf_input_data: number[];\n vrf_output: number[];\n vrf_proof: number[];\n public_key: number[];\n user_id: string;\n rp_id: string;\n block_height: number;\n block_hash: number[];\n intent_digest_32: number[];\n };\n webauthn_registration: WebAuthnRegistrationCredential;\n deterministic_vrf_public_key: number[];\n authenticator_options?: AuthenticatorOptions;\n}\n\n/**\n * Create account and register user using relay-server atomic endpoint\n * Makes a single call to the relay-server's /create_account_and_register_user endpoint\n * which calls the contract's atomic create_account_and_register_user function\n */\nexport async function createAccountAndRegisterWithRelayServer(\n context: PasskeyManagerContext,\n nearAccountId: string,\n publicKey: string,\n credential: WebAuthnRegistrationCredential | PublicKeyCredential,\n vrfChallenge: VRFChallenge,\n deterministicVrfPublicKey: string,\n authenticatorOptions?: AuthenticatorOptions,\n onEvent?: (event: RegistrationSSEEvent) => void,\n opts?: {\n thresholdEd25519?: {\n clientVerifyingShareB64u: string;\n };\n },\n): Promise<{\n success: boolean;\n transactionId?: string;\n thresholdEd25519?: {\n publicKey: string;\n relayerKeyId: string;\n relayerVerifyingShareB64u?: string;\n clientParticipantId?: number;\n relayerParticipantId?: number;\n participantIds?: number[];\n };\n error?: string;\n}> {\n const { configs } = context;\n\n if (!configs.relayer.url) {\n throw new Error('Relay server URL is required for atomic registration');\n }\n\n try {\n onEvent?.({\n step: 4,\n phase: RegistrationPhase.STEP_4_ACCESS_KEY_ADDITION,\n status: RegistrationStatus.PROGRESS,\n message: 'Creating account and adding access key...',\n });\n\n // Serialize the WebAuthn credential properly for the contract.\n // Accept both live PublicKeyCredential and already-serialized credentials from secureConfirm.\n const isSerialized = isSerializedRegistrationCredential(credential);\n\n // Ensure proper serialization + normalization regardless of source\n const serialized: WebAuthnRegistrationCredential = isSerialized\n ? normalizeRegistrationCredential(credential)\n : serializeRegistrationCredential(credential);\n\n // Strip PRF outputs before sending to relay/contract\n const serializedCredential = removePrfOutputGuard<WebAuthnRegistrationCredential>(serialized);\n // Normalize transports to an array (avoid null)\n if (!Array.isArray(serializedCredential?.response?.transports)) {\n serializedCredential.response.transports = [];\n }\n\n // Prepare data for atomic endpoint\n const intent_digest_32 = Array.from(base64UrlDecode(vrfChallenge.intentDigest || ''));\n if (intent_digest_32.length !== 32) {\n throw new Error('Missing or invalid vrfChallenge.intentDigest (expected base64url-encoded 32 bytes)');\n }\n const requestData: CreateAccountAndRegisterUserRequest = {\n new_account_id: nearAccountId,\n new_public_key: publicKey,\n device_number: 1, // First device gets device number 1 (1-indexed)\n ...(opts?.thresholdEd25519?.clientVerifyingShareB64u\n ? {\n threshold_ed25519: {\n client_verifying_share_b64u: opts.thresholdEd25519.clientVerifyingShareB64u,\n },\n }\n : {}),\n vrf_data: {\n vrf_input_data: Array.from(base64UrlDecode(vrfChallenge.vrfInput)),\n vrf_output: Array.from(base64UrlDecode(vrfChallenge.vrfOutput)),\n vrf_proof: Array.from(base64UrlDecode(vrfChallenge.vrfProof)),\n public_key: Array.from(base64UrlDecode(vrfChallenge.vrfPublicKey)),\n user_id: vrfChallenge.userId,\n rp_id: vrfChallenge.rpId,\n block_height: Number(vrfChallenge.blockHeight),\n block_hash: Array.from(base64UrlDecode(vrfChallenge.blockHash)),\n intent_digest_32,\n },\n webauthn_registration: serializedCredential,\n deterministic_vrf_public_key: Array.from(base64UrlDecode(deterministicVrfPublicKey)),\n authenticator_options: authenticatorOptions || context.configs.authenticatorOptions,\n };\n\n onEvent?.({\n step: 5,\n phase: RegistrationPhase.STEP_5_CONTRACT_REGISTRATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Registering user with Web3Authn contract...',\n });\n\n // Call the atomic endpoint\n const response = await fetch(`${configs.relayer.url}/create_account_and_register_user`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestData)\n });\n\n // Handle both successful and failed responses\n const result: CreateAccountAndRegisterResult = await response.json();\n\n if (!response.ok) {\n // Extract specific error message from relay server response\n const errorMessage = result.error || result.message || `HTTP ${response.status}: ${response.statusText}`;\n throw new Error(errorMessage);\n }\n\n if (!result.success) {\n throw new Error(result.error || 'Atomic registration failed');\n }\n\n onEvent?.({\n step: 5,\n phase: RegistrationPhase.STEP_5_CONTRACT_REGISTRATION,\n status: RegistrationStatus.SUCCESS,\n message: 'User registered with Web3Authn contract successfully',\n });\n\n return {\n success: true,\n transactionId: result.transactionHash,\n thresholdEd25519: result.thresholdEd25519\n ? {\n publicKey: result.thresholdEd25519.publicKey,\n relayerKeyId: result.thresholdEd25519.relayerKeyId,\n relayerVerifyingShareB64u: result.thresholdEd25519.relayerVerifyingShareB64u,\n clientParticipantId: result.thresholdEd25519.clientParticipantId,\n relayerParticipantId: result.thresholdEd25519.relayerParticipantId,\n participantIds: result.thresholdEd25519.participantIds,\n }\n : undefined,\n };\n\n } catch (error: unknown) {\n console.error('Atomic registration failed:', error);\n\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: 'Registration failed',\n error: errorMessage(error),\n });\n\n return {\n success: false,\n error: errorMessage(error),\n };\n }\n}\n"],"mappings":";;;;;;;AAWA,SAAS,mCACP,YAC8C;AAC9C,KAAI,CAACA,4BAAS,YAAa,QAAO;CAClC,MAAM,OAAQ,WAAsC;AACpD,KAAI,CAACA,4BAAS,MAAO,QAAO;AAC5B,QAAO,OAAQ,KAAyC,sBAAsB;;;;;;;AAkChF,eAAsB,wCACpB,SACA,eACA,WACA,YACA,cACA,2BACA,sBACA,SACA,MAiBC;CACD,MAAM,EAAE,YAAY;AAEpB,KAAI,CAAC,QAAQ,QAAQ,IACnB,OAAM,IAAI,MAAM;AAGlB,KAAI;AACF,YAAU;GACR,MAAM;GACN,OAAOC,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;EAKX,MAAM,eAAe,mCAAmC;EAGxD,MAAMC,aAA6C,eAC/CC,2DAAgC,cAChCC,2DAAgC;EAGpC,MAAM,uBAAuBC,gDAAqD;AAElF,MAAI,CAAC,MAAM,QAAQ,sBAAsB,UAAU,YACjD,sBAAqB,SAAS,aAAa;EAI7C,MAAM,mBAAmB,MAAM,KAAKC,+BAAgB,aAAa,gBAAgB;AACjF,MAAI,iBAAiB,WAAW,GAC9B,OAAM,IAAI,MAAM;EAElB,MAAMC,cAAmD;GACvD,gBAAgB;GAChB,gBAAgB;GAChB,eAAe;GACf,GAAI,MAAM,kBAAkB,2BACxB,EACA,mBAAmB,EACjB,6BAA6B,KAAK,iBAAiB,+BAGrD;GACJ,UAAU;IACR,gBAAgB,MAAM,KAAKD,+BAAgB,aAAa;IACxD,YAAY,MAAM,KAAKA,+BAAgB,aAAa;IACpD,WAAW,MAAM,KAAKA,+BAAgB,aAAa;IACnD,YAAY,MAAM,KAAKA,+BAAgB,aAAa;IACpD,SAAS,aAAa;IACtB,OAAO,aAAa;IACpB,cAAc,OAAO,aAAa;IAClC,YAAY,MAAM,KAAKA,+BAAgB,aAAa;IACpD;;GAEF,uBAAuB;GACvB,8BAA8B,MAAM,KAAKA,+BAAgB;GACzD,uBAAuB,wBAAwB,QAAQ,QAAQ;;AAGjE,YAAU;GACR,MAAM;GACN,OAAON,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;EAIX,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,IAAI,oCAAoC;GACtF,QAAQ;GACR,SAAS,EAAE,gBAAgB;GAC3B,MAAM,KAAK,UAAU;;EAIvB,MAAMO,SAAyC,MAAM,SAAS;AAE9D,MAAI,CAAC,SAAS,IAAI;GAEhB,MAAMC,iBAAe,OAAO,SAAS,OAAO,WAAW,QAAQ,SAAS,OAAO,IAAI,SAAS;AAC5F,SAAM,IAAI,MAAMA;;AAGlB,MAAI,CAAC,OAAO,QACV,OAAM,IAAI,MAAM,OAAO,SAAS;AAGlC,YAAU;GACR,MAAM;GACN,OAAOT,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;AAGX,SAAO;GACL,SAAS;GACT,eAAe,OAAO;GACtB,kBAAkB,OAAO,mBACrB;IACA,WAAW,OAAO,iBAAiB;IACnC,cAAc,OAAO,iBAAiB;IACtC,2BAA2B,OAAO,iBAAiB;IACnD,qBAAqB,OAAO,iBAAiB;IAC7C,sBAAsB,OAAO,iBAAiB;IAC9C,gBAAgB,OAAO,iBAAiB;OAExC;;UAGCS,OAAgB;AACvB,UAAQ,MAAM,+BAA+B;AAE7C,YAAU;GACR,MAAM;GACN,OAAOV,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;GACT,OAAOQ,4BAAa;;AAGtB,SAAO;GACL,SAAS;GACT,OAAOA,4BAAa"}
|
|
1
|
+
{"version":3,"file":"createAccountRelayServer.js","names":["isObject","RegistrationPhase","RegistrationStatus","serialized: WebAuthnRegistrationCredential","normalizeRegistrationCredential","serializeRegistrationCredential","removePrfOutputGuard","base64UrlDecode","requestData: CreateAccountAndRegisterUserRequest","result: CreateAccountAndRegisterResult","errorMessage","error: unknown"],"sources":["../../../../../src/core/TatchiPasskey/faucets/createAccountRelayServer.ts"],"sourcesContent":["import { VRFChallenge } from '../../types/vrf-worker';\nimport { RegistrationSSEEvent, RegistrationPhase, RegistrationStatus } from '../../types/sdkSentEvents';\nimport { PasskeyManagerContext } from '..';\nimport { base64UrlDecode } from '../../../utils/encoders';\nimport { removePrfOutputGuard, serializeRegistrationCredential, normalizeRegistrationCredential } from '../../WebAuthnManager/credentialsHelpers';\nimport type { WebAuthnRegistrationCredential } from '../../types/webauthn';\nimport type { AuthenticatorOptions } from '../../types/authenticatorOptions';\nimport type { CreateAccountAndRegisterResult } from '../../../server/core/types';\nimport { isObject } from '@/utils/validation';\nimport { errorMessage } from '../../../utils/errors';\n\nfunction isSerializedRegistrationCredential(\n credential: WebAuthnRegistrationCredential | PublicKeyCredential,\n): credential is WebAuthnRegistrationCredential {\n if (!isObject(credential)) return false;\n const resp = (credential as { response?: unknown }).response;\n if (!isObject(resp)) return false;\n return typeof (resp as { attestationObject?: unknown }).attestationObject === 'string';\n}\n\n/**\n * HTTP Request body for the relay server's /create_account_and_register_user endpoint\n */\nexport interface CreateAccountAndRegisterUserRequest {\n new_account_id: string;\n new_public_key: string;\n device_number: number;\n threshold_ed25519?: {\n client_verifying_share_b64u: string;\n };\n vrf_data: {\n // Send compact base64url strings; the relay normalizes to byte arrays for contract calls.\n vrf_input_data: string;\n vrf_output: string;\n vrf_proof: string;\n public_key: string;\n user_id: string;\n rp_id: string;\n block_height: number;\n block_hash: string;\n intent_digest_32: string;\n };\n webauthn_registration: WebAuthnRegistrationCredential;\n deterministic_vrf_public_key: number[];\n authenticator_options?: AuthenticatorOptions;\n}\n\n/**\n * Create account and register user using relay-server atomic endpoint\n * Makes a single call to the relay-server's /create_account_and_register_user endpoint\n * which calls the contract's atomic create_account_and_register_user function\n */\nexport async function createAccountAndRegisterWithRelayServer(\n context: PasskeyManagerContext,\n nearAccountId: string,\n publicKey: string,\n credential: WebAuthnRegistrationCredential | PublicKeyCredential,\n vrfChallenge: VRFChallenge,\n deterministicVrfPublicKey: string,\n authenticatorOptions?: AuthenticatorOptions,\n onEvent?: (event: RegistrationSSEEvent) => void,\n opts?: {\n thresholdEd25519?: {\n clientVerifyingShareB64u: string;\n };\n },\n): Promise<{\n success: boolean;\n transactionId?: string;\n thresholdEd25519?: {\n publicKey: string;\n relayerKeyId: string;\n relayerVerifyingShareB64u?: string;\n clientParticipantId?: number;\n relayerParticipantId?: number;\n participantIds?: number[];\n };\n error?: string;\n}> {\n const { configs } = context;\n\n if (!configs.relayer.url) {\n throw new Error('Relay server URL is required for atomic registration');\n }\n\n try {\n onEvent?.({\n step: 4,\n phase: RegistrationPhase.STEP_4_ACCESS_KEY_ADDITION,\n status: RegistrationStatus.PROGRESS,\n message: 'Creating account and adding access key...',\n });\n\n // Serialize the WebAuthn credential properly for the contract.\n // Accept both live PublicKeyCredential and already-serialized credentials from secureConfirm.\n const isSerialized = isSerializedRegistrationCredential(credential);\n\n // Ensure proper serialization + normalization regardless of source\n const serialized: WebAuthnRegistrationCredential = isSerialized\n ? normalizeRegistrationCredential(credential)\n : serializeRegistrationCredential(credential);\n\n // Strip PRF outputs before sending to relay/contract\n const serializedCredential = removePrfOutputGuard<WebAuthnRegistrationCredential>(serialized);\n // Normalize transports to an array (avoid null)\n if (!Array.isArray(serializedCredential?.response?.transports)) {\n serializedCredential.response.transports = [];\n }\n\n // Prepare data for atomic endpoint\n const intentDigestB64u = String(vrfChallenge.intentDigest || '').trim();\n const intentDigestBytes = base64UrlDecode(intentDigestB64u);\n if (intentDigestBytes.length !== 32) {\n throw new Error('Missing or invalid vrfChallenge.intentDigest (expected base64url-encoded 32 bytes)');\n }\n const requestData: CreateAccountAndRegisterUserRequest = {\n new_account_id: nearAccountId,\n new_public_key: publicKey,\n device_number: 1, // First device gets device number 1 (1-indexed)\n ...(opts?.thresholdEd25519?.clientVerifyingShareB64u\n ? {\n threshold_ed25519: {\n client_verifying_share_b64u: opts.thresholdEd25519.clientVerifyingShareB64u,\n },\n }\n : {}),\n vrf_data: {\n vrf_input_data: vrfChallenge.vrfInput,\n vrf_output: vrfChallenge.vrfOutput,\n vrf_proof: vrfChallenge.vrfProof,\n public_key: vrfChallenge.vrfPublicKey,\n user_id: vrfChallenge.userId,\n rp_id: vrfChallenge.rpId,\n block_height: Number(vrfChallenge.blockHeight),\n block_hash: vrfChallenge.blockHash,\n intent_digest_32: intentDigestB64u,\n },\n webauthn_registration: serializedCredential,\n deterministic_vrf_public_key: Array.from(base64UrlDecode(deterministicVrfPublicKey)),\n authenticator_options: authenticatorOptions || context.configs.authenticatorOptions,\n };\n\n onEvent?.({\n step: 5,\n phase: RegistrationPhase.STEP_5_CONTRACT_REGISTRATION,\n status: RegistrationStatus.PROGRESS,\n message: 'Registering user with Web3Authn contract...',\n });\n\n // Call the atomic endpoint\n const response = await fetch(`${configs.relayer.url}/create_account_and_register_user`, {\n method: 'POST',\n headers: { 'Content-Type': 'application/json' },\n body: JSON.stringify(requestData)\n });\n\n // Handle both successful and failed responses\n const result: CreateAccountAndRegisterResult = await response.json();\n\n if (!response.ok) {\n // Extract specific error message from relay server response\n const errorMessage = result.error || result.message || `HTTP ${response.status}: ${response.statusText}`;\n throw new Error(errorMessage);\n }\n\n if (!result.success) {\n throw new Error(result.error || 'Atomic registration failed');\n }\n\n onEvent?.({\n step: 5,\n phase: RegistrationPhase.STEP_5_CONTRACT_REGISTRATION,\n status: RegistrationStatus.SUCCESS,\n message: 'User registered with Web3Authn contract successfully',\n });\n\n return {\n success: true,\n transactionId: result.transactionHash,\n thresholdEd25519: result.thresholdEd25519\n ? {\n publicKey: result.thresholdEd25519.publicKey,\n relayerKeyId: result.thresholdEd25519.relayerKeyId,\n relayerVerifyingShareB64u: result.thresholdEd25519.relayerVerifyingShareB64u,\n clientParticipantId: result.thresholdEd25519.clientParticipantId,\n relayerParticipantId: result.thresholdEd25519.relayerParticipantId,\n participantIds: result.thresholdEd25519.participantIds,\n }\n : undefined,\n };\n\n } catch (error: unknown) {\n console.error('Atomic registration failed:', error);\n\n onEvent?.({\n step: 0,\n phase: RegistrationPhase.REGISTRATION_ERROR,\n status: RegistrationStatus.ERROR,\n message: 'Registration failed',\n error: errorMessage(error),\n });\n\n return {\n success: false,\n error: errorMessage(error),\n };\n }\n}\n"],"mappings":";;;;;;;AAWA,SAAS,mCACP,YAC8C;AAC9C,KAAI,CAACA,4BAAS,YAAa,QAAO;CAClC,MAAM,OAAQ,WAAsC;AACpD,KAAI,CAACA,4BAAS,MAAO,QAAO;AAC5B,QAAO,OAAQ,KAAyC,sBAAsB;;;;;;;AAmChF,eAAsB,wCACpB,SACA,eACA,WACA,YACA,cACA,2BACA,sBACA,SACA,MAiBC;CACD,MAAM,EAAE,YAAY;AAEpB,KAAI,CAAC,QAAQ,QAAQ,IACnB,OAAM,IAAI,MAAM;AAGlB,KAAI;AACF,YAAU;GACR,MAAM;GACN,OAAOC,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;EAKX,MAAM,eAAe,mCAAmC;EAGxD,MAAMC,aAA6C,eAC/CC,2DAAgC,cAChCC,2DAAgC;EAGpC,MAAM,uBAAuBC,gDAAqD;AAElF,MAAI,CAAC,MAAM,QAAQ,sBAAsB,UAAU,YACjD,sBAAqB,SAAS,aAAa;EAI7C,MAAM,mBAAmB,OAAO,aAAa,gBAAgB,IAAI;EACjE,MAAM,oBAAoBC,+BAAgB;AAC1C,MAAI,kBAAkB,WAAW,GAC/B,OAAM,IAAI,MAAM;EAElB,MAAMC,cAAmD;GACvD,gBAAgB;GAChB,gBAAgB;GAChB,eAAe;GACf,GAAI,MAAM,kBAAkB,2BACxB,EACA,mBAAmB,EACjB,6BAA6B,KAAK,iBAAiB,+BAGrD;GACJ,UAAU;IACR,gBAAgB,aAAa;IAC7B,YAAY,aAAa;IACzB,WAAW,aAAa;IACxB,YAAY,aAAa;IACzB,SAAS,aAAa;IACtB,OAAO,aAAa;IACpB,cAAc,OAAO,aAAa;IAClC,YAAY,aAAa;IACzB,kBAAkB;;GAEpB,uBAAuB;GACvB,8BAA8B,MAAM,KAAKD,+BAAgB;GACzD,uBAAuB,wBAAwB,QAAQ,QAAQ;;AAGjE,YAAU;GACR,MAAM;GACN,OAAON,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;EAIX,MAAM,WAAW,MAAM,MAAM,GAAG,QAAQ,QAAQ,IAAI,oCAAoC;GACtF,QAAQ;GACR,SAAS,EAAE,gBAAgB;GAC3B,MAAM,KAAK,UAAU;;EAIvB,MAAMO,SAAyC,MAAM,SAAS;AAE9D,MAAI,CAAC,SAAS,IAAI;GAEhB,MAAMC,iBAAe,OAAO,SAAS,OAAO,WAAW,QAAQ,SAAS,OAAO,IAAI,SAAS;AAC5F,SAAM,IAAI,MAAMA;;AAGlB,MAAI,CAAC,OAAO,QACV,OAAM,IAAI,MAAM,OAAO,SAAS;AAGlC,YAAU;GACR,MAAM;GACN,OAAOT,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;;AAGX,SAAO;GACL,SAAS;GACT,eAAe,OAAO;GACtB,kBAAkB,OAAO,mBACrB;IACA,WAAW,OAAO,iBAAiB;IACnC,cAAc,OAAO,iBAAiB;IACtC,2BAA2B,OAAO,iBAAiB;IACnD,qBAAqB,OAAO,iBAAiB;IAC7C,sBAAsB,OAAO,iBAAiB;IAC9C,gBAAgB,OAAO,iBAAiB;OAExC;;UAGCS,OAAgB;AACvB,UAAQ,MAAM,+BAA+B;AAE7C,YAAU;GACR,MAAM;GACN,OAAOV,wCAAkB;GACzB,QAAQC,yCAAmB;GAC3B,SAAS;GACT,OAAOQ,4BAAa;;AAGtB,SAAO;GACL,SAAS;GACT,OAAOA,4BAAa"}
|
|
@@ -532,7 +532,7 @@ async function handleLoginUnlockVRF(context, nearAccountId, onEvent, onError, af
|
|
|
532
532
|
const relayerUrl$1 = context.configs.relayer?.url;
|
|
533
533
|
if (usedFallbackTouchId && relayerUrl$1) {
|
|
534
534
|
const refreshed = await webAuthnManager.shamir3PassEncryptCurrentVrfKeypair();
|
|
535
|
-
await webAuthnManager.updateServerEncryptedVrfKeypair(nearAccountId, refreshed);
|
|
535
|
+
await webAuthnManager.updateServerEncryptedVrfKeypair(nearAccountId, refreshed, activeDeviceNumber);
|
|
536
536
|
}
|
|
537
537
|
} catch (refreshErr) {
|
|
538
538
|
console.warn("Non-fatal: Failed to refresh serverEncryptedVrfKeypair:", refreshErr?.message || refreshErr);
|