@tatchi-xyz/sdk 0.17.0 → 0.18.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/cjs/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/core/rpcCalls.js +8 -0
- package/dist/cjs/core/rpcCalls.js.map +1 -1
- package/dist/cjs/index.js +6 -2
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → cjs/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{esm/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → cjs/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/cjs/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/cjs/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/cjs/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/cjs/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +69 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js +32 -20
- package/dist/cjs/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js +1 -0
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/cjs/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js +6 -2
- package/dist/cjs/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/cjs/react/sdk/src/core/rpcCalls.js +8 -0
- package/dist/cjs/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/core/EmailRecovery/index.js +28 -21
- package/dist/esm/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/esm/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/core/rpcCalls.js +8 -1
- package/dist/esm/core/rpcCalls.js.map +1 -1
- package/dist/esm/index.js +4 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/react/components/AccountMenuButton/{LinkedDevicesModal-B6api181.css → LinkedDevicesModal-CSSowiHP.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/LinkedDevicesModal-B6api181.css.map → esm/react/components/AccountMenuButton/LinkedDevicesModal-CSSowiHP.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{ProfileDropdown-B-DrG_u5.css → ProfileDropdown-CEPMZ1gY.css} +1 -1
- package/dist/{cjs/react/components/AccountMenuButton/ProfileDropdown-B-DrG_u5.css.map → esm/react/components/AccountMenuButton/ProfileDropdown-CEPMZ1gY.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css → Web3AuthProfileButton-DopOg7Xc.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/{Web3AuthProfileButton-BnZDUeCL.css.map → Web3AuthProfileButton-DopOg7Xc.css.map} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css → TouchIcon-BQWentvJ.css} +1 -1
- package/dist/esm/react/components/AccountMenuButton/icons/{TouchIcon-CAGCi8MY.css.map → TouchIcon-BQWentvJ.css.map} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css → PasskeyAuthMenu-DwrzWMYx.css} +1 -1
- package/dist/esm/react/components/PasskeyAuthMenu/{PasskeyAuthMenu-CNNxVj4L.css.map → PasskeyAuthMenu-DwrzWMYx.css.map} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css → ShowQRCode-CCN4h6Uv.css} +1 -1
- package/dist/esm/react/components/{ShowQRCode-nZhZSaba.css.map → ShowQRCode-CCN4h6Uv.css.map} +1 -1
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js +27 -32
- package/dist/esm/react/hooks/usePreconnectWalletAssets.js.map +1 -1
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js +63 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/emailRecoveryPendingStore.js.map +1 -0
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js +28 -21
- package/dist/esm/react/sdk/src/core/EmailRecovery/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js +519 -448
- package/dist/esm/react/sdk/src/core/TatchiPasskey/emailRecovery.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js +2 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/index.js.map +1 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js +23 -1
- package/dist/esm/react/sdk/src/core/TatchiPasskey/relay.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js +0 -7
- package/dist/esm/react/sdk/src/core/WalletIframe/client/IframeTransport.js.map +1 -1
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js +7 -3
- package/dist/esm/react/sdk/src/core/WalletIframe/client/router.js.map +1 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js +8 -1
- package/dist/esm/react/sdk/src/core/rpcCalls.js.map +1 -1
- package/dist/esm/sdk/offline-export-app.js.map +1 -1
- package/dist/esm/sdk/{router-BLFegW7J.js → router-DuGYOd3G.js} +6 -9
- package/dist/esm/sdk/{rpcCalls-DEv9x5-f.js → rpcCalls-BQrJMTdg.js} +2 -2
- package/dist/esm/sdk/{rpcCalls-OhgEeFig.js → rpcCalls-YVeUVMk2.js} +8 -1
- package/dist/esm/sdk/wallet-iframe-host.js +624 -471
- package/dist/esm/wasm_vrf_worker/pkg/wasm_vrf_worker_bg.wasm +0 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts +25 -0
- package/dist/types/src/core/EmailRecovery/emailRecoveryPendingStore.d.ts.map +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts +1 -0
- package/dist/types/src/core/EmailRecovery/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts +35 -6
- package/dist/types/src/core/TatchiPasskey/emailRecovery.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/index.d.ts +2 -2
- package/dist/types/src/core/TatchiPasskey/index.d.ts.map +1 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts +2 -1
- package/dist/types/src/core/TatchiPasskey/relay.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/IframeTransport.d.ts.map +1 -1
- package/dist/types/src/core/WalletIframe/client/router.d.ts +3 -3
- package/dist/types/src/core/WalletIframe/client/router.d.ts.map +1 -1
- package/dist/types/src/core/rpcCalls.d.ts +9 -0
- package/dist/types/src/core/rpcCalls.d.ts.map +1 -1
- package/dist/types/src/index.d.ts +1 -0
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/react/hooks/usePreconnectWalletAssets.d.ts.map +1 -1
- package/dist/workers/wasm_vrf_worker_bg.wasm +0 -0
- package/package.json +1 -1
|
@@ -15,7 +15,7 @@ import { MinimalNearClient, SignedTransaction, isOffline, openOfflineExport } fr
|
|
|
15
15
|
import { AccountRecoveryPhase, AccountRecoveryStatus, ActionPhase, ActionStatus, DEFAULT_WAIT_STATUS, DeviceLinkingPhase, DeviceLinkingStatus, EmailRecoveryPhase, EmailRecoveryStatus, LoginPhase, LoginStatus, RegistrationPhase, RegistrationStatus, init_rpc, init_sdkSentEvents } from "./sdkSentEvents-_jrJLIhw.js";
|
|
16
16
|
import { SecureConfirmMessageType, SecureConfirmationType, TouchIdPrompt, authenticatorsToAllowCredentials, createRandomVRFChallenge, getIntentDigest, init_accountIds, init_touchIdPrompt, init_vrf_worker, parseTransactionSummary, sanitizeForPostMessage, sendConfirmResponse, toAccountId, validateVRFChallenge } from "./requestHelpers-Dh1hEYL9.js";
|
|
17
17
|
import { PASSKEY_MANAGER_DEFAULT_CONFIGS, buildConfigsFromEnv, init_defaultConfigs } from "./defaultConfigs-DpslkAQd.js";
|
|
18
|
-
import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-
|
|
18
|
+
import { buildSetRecoveryEmailsActions, checkCanRegisterUserContractCall, executeDeviceLinkingContractCalls, getCredentialIdsContractCall, getDeviceLinkingAccountContractCall, getRecoveryEmailHashesContractCall, init_rpcCalls, syncAuthenticatorsContractCall, verifyAuthenticationResponse } from "./rpcCalls-YVeUVMk2.js";
|
|
19
19
|
import { getLastLoggedInDeviceNumber, init_getDeviceNumber, parseDeviceNumber } from "./getDeviceNumber-zsOHT_Um.js";
|
|
20
20
|
|
|
21
21
|
//#region src/core/sdkPaths/workers.ts
|
|
@@ -9845,8 +9845,30 @@ async function signDelegateAction(args) {
|
|
|
9845
9845
|
//#endregion
|
|
9846
9846
|
//#region src/core/TatchiPasskey/relay.ts
|
|
9847
9847
|
init_sdkSentEvents();
|
|
9848
|
+
const toNumberArray = (value) => Array.isArray(value) ? value : Array.from(value);
|
|
9849
|
+
const normalizeSignedDelegateForRelay = (signedDelegate) => {
|
|
9850
|
+
const delegateAction = signedDelegate.delegateAction;
|
|
9851
|
+
const signature = signedDelegate.signature;
|
|
9852
|
+
return {
|
|
9853
|
+
delegateAction: {
|
|
9854
|
+
...delegateAction,
|
|
9855
|
+
publicKey: {
|
|
9856
|
+
...delegateAction.publicKey,
|
|
9857
|
+
keyData: toNumberArray(delegateAction.publicKey.keyData)
|
|
9858
|
+
}
|
|
9859
|
+
},
|
|
9860
|
+
signature: {
|
|
9861
|
+
...signature,
|
|
9862
|
+
signatureData: toNumberArray(signature.signatureData)
|
|
9863
|
+
}
|
|
9864
|
+
};
|
|
9865
|
+
};
|
|
9848
9866
|
async function sendDelegateActionViaRelayer(args) {
|
|
9849
9867
|
const { url, payload, signal, options } = args;
|
|
9868
|
+
const normalizedPayload = {
|
|
9869
|
+
...payload,
|
|
9870
|
+
signedDelegate: normalizeSignedDelegateForRelay(payload.signedDelegate)
|
|
9871
|
+
};
|
|
9850
9872
|
const emit = (event) => options?.onEvent?.(event);
|
|
9851
9873
|
const emitError = (message) => {
|
|
9852
9874
|
emit({
|
|
@@ -9868,7 +9890,7 @@ async function sendDelegateActionViaRelayer(args) {
|
|
|
9868
9890
|
res = await fetch(url, {
|
|
9869
9891
|
method: "POST",
|
|
9870
9892
|
headers: { "content-type": "application/json" },
|
|
9871
|
-
body: JSON.stringify(
|
|
9893
|
+
body: JSON.stringify(normalizedPayload),
|
|
9872
9894
|
signal
|
|
9873
9895
|
});
|
|
9874
9896
|
} catch (err$1) {
|
|
@@ -9933,28 +9955,65 @@ const OFFLINE_EXPORT_FALLBACK = "OFFLINE_EXPORT_FALLBACK";
|
|
|
9933
9955
|
const WALLET_UI_CLOSED = "WALLET_UI_CLOSED";
|
|
9934
9956
|
const EXPORT_NEAR_KEYPAIR_CANCELLED = "EXPORT_NEAR_KEYPAIR_CANCELLED";
|
|
9935
9957
|
|
|
9958
|
+
//#endregion
|
|
9959
|
+
//#region src/core/EmailRecovery/emailRecoveryPendingStore.ts
|
|
9960
|
+
var EmailRecoveryPendingStore;
|
|
9961
|
+
var init_emailRecoveryPendingStore = __esm({ "src/core/EmailRecovery/emailRecoveryPendingStore.ts": (() => {
|
|
9962
|
+
init_IndexedDBManager();
|
|
9963
|
+
EmailRecoveryPendingStore = class {
|
|
9964
|
+
getPendingTtlMs;
|
|
9965
|
+
now;
|
|
9966
|
+
constructor(options) {
|
|
9967
|
+
this.getPendingTtlMs = options.getPendingTtlMs;
|
|
9968
|
+
this.now = options.now ?? Date.now;
|
|
9969
|
+
}
|
|
9970
|
+
getPendingIndexKey(accountId) {
|
|
9971
|
+
return `pendingEmailRecovery:${accountId}`;
|
|
9972
|
+
}
|
|
9973
|
+
getPendingRecordKey(accountId, nearPublicKey) {
|
|
9974
|
+
return `${this.getPendingIndexKey(accountId)}:${nearPublicKey}`;
|
|
9975
|
+
}
|
|
9976
|
+
async get(accountId, nearPublicKey) {
|
|
9977
|
+
const pendingTtlMs = this.getPendingTtlMs();
|
|
9978
|
+
const indexKey = this.getPendingIndexKey(accountId);
|
|
9979
|
+
const indexedNearPublicKey = await IndexedDBManager.clientDB.getAppState(indexKey);
|
|
9980
|
+
const resolvedNearPublicKey = nearPublicKey ?? indexedNearPublicKey;
|
|
9981
|
+
if (!resolvedNearPublicKey) return null;
|
|
9982
|
+
const recordKey = this.getPendingRecordKey(accountId, resolvedNearPublicKey);
|
|
9983
|
+
const record = await IndexedDBManager.clientDB.getAppState(recordKey);
|
|
9984
|
+
const shouldClearIndex = indexedNearPublicKey === resolvedNearPublicKey;
|
|
9985
|
+
if (!record) {
|
|
9986
|
+
if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
9987
|
+
return null;
|
|
9988
|
+
}
|
|
9989
|
+
if (this.now() - record.createdAt > pendingTtlMs) {
|
|
9990
|
+
await IndexedDBManager.clientDB.setAppState(recordKey, void 0).catch(() => {});
|
|
9991
|
+
if (shouldClearIndex) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
9992
|
+
return null;
|
|
9993
|
+
}
|
|
9994
|
+
await this.touchIndex(accountId, record.nearPublicKey);
|
|
9995
|
+
return record;
|
|
9996
|
+
}
|
|
9997
|
+
async set(record) {
|
|
9998
|
+
const key = this.getPendingRecordKey(record.accountId, record.nearPublicKey);
|
|
9999
|
+
await IndexedDBManager.clientDB.setAppState(key, record);
|
|
10000
|
+
await this.touchIndex(record.accountId, record.nearPublicKey);
|
|
10001
|
+
}
|
|
10002
|
+
async clear(accountId, nearPublicKey) {
|
|
10003
|
+
const indexKey = this.getPendingIndexKey(accountId);
|
|
10004
|
+
const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
|
|
10005
|
+
const resolvedNearPublicKey = nearPublicKey || idx || "";
|
|
10006
|
+
if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
|
|
10007
|
+
if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10008
|
+
}
|
|
10009
|
+
async touchIndex(accountId, nearPublicKey) {
|
|
10010
|
+
await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(accountId), nearPublicKey).catch(() => {});
|
|
10011
|
+
}
|
|
10012
|
+
};
|
|
10013
|
+
}) });
|
|
10014
|
+
|
|
9936
10015
|
//#endregion
|
|
9937
10016
|
//#region src/core/EmailRecovery/index.ts
|
|
9938
|
-
init_accountIds();
|
|
9939
|
-
init_IndexedDBManager();
|
|
9940
|
-
const canonicalizeEmail = (email) => {
|
|
9941
|
-
const raw = String(email || "").trim();
|
|
9942
|
-
if (!raw) return "";
|
|
9943
|
-
const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
|
|
9944
|
-
const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
|
|
9945
|
-
const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
|
|
9946
|
-
const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
|
|
9947
|
-
for (const candidate of candidates) {
|
|
9948
|
-
const cleaned = candidate.replace(/^mailto:\s*/i, "");
|
|
9949
|
-
const match = cleaned.match(emailRegex);
|
|
9950
|
-
if (match?.[1]) return match[1].trim().toLowerCase();
|
|
9951
|
-
}
|
|
9952
|
-
return withoutHeaderName.toLowerCase();
|
|
9953
|
-
};
|
|
9954
|
-
const bytesToHex = (bytes) => {
|
|
9955
|
-
const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
|
|
9956
|
-
return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
9957
|
-
};
|
|
9958
10017
|
async function hashRecoveryEmails(emails, accountId) {
|
|
9959
10018
|
const encoder = new TextEncoder();
|
|
9960
10019
|
const salt = (accountId || "").trim().toLowerCase();
|
|
@@ -10001,6 +10060,30 @@ async function prepareRecoveryEmails(nearAccountId, recoveryEmails) {
|
|
|
10001
10060
|
async function getLocalRecoveryEmails(nearAccountId) {
|
|
10002
10061
|
return IndexedDBManager.getRecoveryEmails(nearAccountId);
|
|
10003
10062
|
}
|
|
10063
|
+
var canonicalizeEmail, bytesToHex;
|
|
10064
|
+
var init_EmailRecovery = __esm({ "src/core/EmailRecovery/index.ts": (() => {
|
|
10065
|
+
init_accountIds();
|
|
10066
|
+
init_IndexedDBManager();
|
|
10067
|
+
init_emailRecoveryPendingStore();
|
|
10068
|
+
canonicalizeEmail = (email) => {
|
|
10069
|
+
const raw = String(email || "").trim();
|
|
10070
|
+
if (!raw) return "";
|
|
10071
|
+
const withoutHeaderName = raw.replace(/^[a-z0-9-]+\s*:\s*/i, "").trim();
|
|
10072
|
+
const emailRegex = /([a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9-]+(?:\.[a-zA-Z0-9-]+)*)/;
|
|
10073
|
+
const angleMatch = withoutHeaderName.match(/<([^>]+)>/);
|
|
10074
|
+
const candidates = [angleMatch?.[1], withoutHeaderName].filter((v) => typeof v === "string" && v.length > 0);
|
|
10075
|
+
for (const candidate of candidates) {
|
|
10076
|
+
const cleaned = candidate.replace(/^mailto:\s*/i, "");
|
|
10077
|
+
const match = cleaned.match(emailRegex);
|
|
10078
|
+
if (match?.[1]) return match[1].trim().toLowerCase();
|
|
10079
|
+
}
|
|
10080
|
+
return withoutHeaderName.toLowerCase();
|
|
10081
|
+
};
|
|
10082
|
+
bytesToHex = (bytes) => {
|
|
10083
|
+
const arr = bytes instanceof Uint8Array ? bytes : Uint8Array.from(bytes);
|
|
10084
|
+
return `0x${Array.from(arr).map((b) => b.toString(16).padStart(2, "0")).join("")}`;
|
|
10085
|
+
};
|
|
10086
|
+
}) });
|
|
10004
10087
|
|
|
10005
10088
|
//#endregion
|
|
10006
10089
|
//#region src/core/TatchiPasskey/emailRecovery.ts
|
|
@@ -10047,9 +10130,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10047
10130
|
init_rpc();
|
|
10048
10131
|
init_getDeviceNumber();
|
|
10049
10132
|
init_login();
|
|
10133
|
+
init_EmailRecovery();
|
|
10050
10134
|
EmailRecoveryFlow = class {
|
|
10051
10135
|
context;
|
|
10052
10136
|
options;
|
|
10137
|
+
pendingStore;
|
|
10053
10138
|
pending = null;
|
|
10054
10139
|
phase = EmailRecoveryPhase.STEP_1_PREPARATION;
|
|
10055
10140
|
pollingTimer;
|
|
@@ -10060,6 +10145,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10060
10145
|
constructor(context, options) {
|
|
10061
10146
|
this.context = context;
|
|
10062
10147
|
this.options = options;
|
|
10148
|
+
this.pendingStore = options?.pendingStore ?? new EmailRecoveryPendingStore({ getPendingTtlMs: () => this.getConfig().pendingTtlMs });
|
|
10063
10149
|
}
|
|
10064
10150
|
setOptions(options) {
|
|
10065
10151
|
if (!options) return;
|
|
@@ -10067,6 +10153,7 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10067
10153
|
...this.options || {},
|
|
10068
10154
|
...options
|
|
10069
10155
|
};
|
|
10156
|
+
if (options.pendingStore) this.pendingStore = options.pendingStore;
|
|
10070
10157
|
}
|
|
10071
10158
|
emit(event) {
|
|
10072
10159
|
this.options?.onEvent?.(event);
|
|
@@ -10085,24 +10172,139 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10085
10172
|
this.options?.onError?.(err$1);
|
|
10086
10173
|
return err$1;
|
|
10087
10174
|
}
|
|
10175
|
+
async fail(step, message) {
|
|
10176
|
+
const err$1 = this.emitError(step, message);
|
|
10177
|
+
await this.options?.afterCall?.(false);
|
|
10178
|
+
throw err$1;
|
|
10179
|
+
}
|
|
10180
|
+
async assertValidAccountIdOrFail(step, accountId) {
|
|
10181
|
+
const validation = validateNearAccountId(accountId);
|
|
10182
|
+
if (!validation.valid) await this.fail(step, `Invalid NEAR account ID: ${validation.error}`);
|
|
10183
|
+
return toAccountId(accountId);
|
|
10184
|
+
}
|
|
10185
|
+
async resolvePendingOrFail(step, args, options) {
|
|
10186
|
+
const { allowErrorStatus = true, missingMessage = "No pending email recovery record found for this account", errorStatusMessage = "Pending email recovery is in an error state; please restart the flow" } = options ?? {};
|
|
10187
|
+
let rec = this.pending;
|
|
10188
|
+
if (!rec || rec.accountId !== args.accountId || args.nearPublicKey && rec.nearPublicKey !== args.nearPublicKey) {
|
|
10189
|
+
rec = await this.loadPending(args.accountId, args.nearPublicKey);
|
|
10190
|
+
this.pending = rec;
|
|
10191
|
+
}
|
|
10192
|
+
if (!rec) await this.fail(step, missingMessage);
|
|
10193
|
+
const resolved = rec;
|
|
10194
|
+
if (!allowErrorStatus && resolved.status === "error") await this.fail(step, errorStatusMessage);
|
|
10195
|
+
return resolved;
|
|
10196
|
+
}
|
|
10088
10197
|
getConfig() {
|
|
10089
10198
|
return getEmailRecoveryConfig(this.context.configs);
|
|
10090
10199
|
}
|
|
10091
|
-
|
|
10092
|
-
return
|
|
10200
|
+
toBigInt(value) {
|
|
10201
|
+
if (typeof value === "bigint") return value;
|
|
10202
|
+
if (typeof value === "number") return BigInt(value);
|
|
10203
|
+
if (typeof value === "string" && value.length > 0) return BigInt(value);
|
|
10204
|
+
return BigInt(0);
|
|
10093
10205
|
}
|
|
10094
|
-
|
|
10095
|
-
|
|
10206
|
+
computeAvailableBalance(accountView) {
|
|
10207
|
+
const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
|
|
10208
|
+
const amount = this.toBigInt(accountView.amount);
|
|
10209
|
+
const locked = this.toBigInt(accountView.locked);
|
|
10210
|
+
const storageUsage = this.toBigInt(accountView.storage_usage);
|
|
10211
|
+
const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
10212
|
+
const rawAvailable = amount - locked - storageCost;
|
|
10213
|
+
return rawAvailable > 0 ? rawAvailable : BigInt(0);
|
|
10214
|
+
}
|
|
10215
|
+
async assertSufficientBalance(nearAccountId) {
|
|
10216
|
+
const { minBalanceYocto } = this.getConfig();
|
|
10217
|
+
try {
|
|
10218
|
+
const accountView = await this.context.nearClient.viewAccount(nearAccountId);
|
|
10219
|
+
const available = this.computeAvailableBalance(accountView);
|
|
10220
|
+
if (available < BigInt(minBalanceYocto)) await this.fail(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
|
|
10221
|
+
} catch (e) {
|
|
10222
|
+
await this.fail(1, e?.message || "Failed to fetch account balance for recovery");
|
|
10223
|
+
}
|
|
10224
|
+
}
|
|
10225
|
+
async getCanonicalRecoveryEmailOrFail(recoveryEmail) {
|
|
10226
|
+
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
10227
|
+
if (!canonicalEmail) await this.fail(1, "Recovery email is required for email-based account recovery");
|
|
10228
|
+
return canonicalEmail;
|
|
10229
|
+
}
|
|
10230
|
+
async getNextDeviceNumberFromContract(nearAccountId) {
|
|
10231
|
+
try {
|
|
10232
|
+
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10233
|
+
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
|
|
10234
|
+
const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
|
|
10235
|
+
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
|
10236
|
+
return max + 1;
|
|
10237
|
+
} catch {
|
|
10238
|
+
return 1;
|
|
10239
|
+
}
|
|
10240
|
+
}
|
|
10241
|
+
async collectRecoveryCredentialOrFail(nearAccountId, deviceNumber) {
|
|
10242
|
+
const confirmerText = {
|
|
10243
|
+
title: this.options?.confirmerText?.title ?? "Register New Recovery Account",
|
|
10244
|
+
body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
|
|
10245
|
+
};
|
|
10246
|
+
const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
|
|
10247
|
+
nearAccountId,
|
|
10248
|
+
deviceNumber,
|
|
10249
|
+
confirmerText,
|
|
10250
|
+
confirmationConfigOverride: this.options?.confirmationConfig
|
|
10251
|
+
});
|
|
10252
|
+
if (!confirm.confirmed || !confirm.credential) await this.fail(2, "User cancelled email recovery TouchID confirmation");
|
|
10253
|
+
return {
|
|
10254
|
+
credential: confirm.credential,
|
|
10255
|
+
vrfChallenge: confirm.vrfChallenge || void 0
|
|
10256
|
+
};
|
|
10257
|
+
}
|
|
10258
|
+
async deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, credential) {
|
|
10259
|
+
const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
|
|
10260
|
+
credential,
|
|
10261
|
+
nearAccountId
|
|
10262
|
+
});
|
|
10263
|
+
if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) await this.fail(2, "Failed to derive VRF keypair from PRF for email recovery");
|
|
10264
|
+
const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
|
|
10265
|
+
nearAccountId,
|
|
10266
|
+
credential,
|
|
10267
|
+
options: { deviceNumber }
|
|
10268
|
+
});
|
|
10269
|
+
if (!nearKeyResult.success || !nearKeyResult.publicKey) await this.fail(2, "Failed to derive NEAR keypair for email recovery");
|
|
10270
|
+
return {
|
|
10271
|
+
encryptedVrfKeypair: vrfDerivationResult.encryptedVrfKeypair,
|
|
10272
|
+
serverEncryptedVrfKeypair: vrfDerivationResult.serverEncryptedVrfKeypair || null,
|
|
10273
|
+
vrfPublicKey: vrfDerivationResult.vrfPublicKey,
|
|
10274
|
+
nearPublicKey: nearKeyResult.publicKey
|
|
10275
|
+
};
|
|
10276
|
+
}
|
|
10277
|
+
emitAwaitEmail(rec, mailtoUrl) {
|
|
10278
|
+
this.phase = EmailRecoveryPhase.STEP_3_AWAIT_EMAIL;
|
|
10279
|
+
this.emit({
|
|
10280
|
+
step: 3,
|
|
10281
|
+
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10282
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10283
|
+
message: "New device key created; please send the recovery email from your registered address.",
|
|
10284
|
+
data: {
|
|
10285
|
+
accountId: rec.accountId,
|
|
10286
|
+
recoveryEmail: rec.recoveryEmail,
|
|
10287
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10288
|
+
requestId: rec.requestId,
|
|
10289
|
+
mailtoUrl
|
|
10290
|
+
}
|
|
10291
|
+
});
|
|
10096
10292
|
}
|
|
10097
|
-
|
|
10293
|
+
emitAutoLoginEvent(status, message, data) {
|
|
10294
|
+
this.emit({
|
|
10295
|
+
step: 5,
|
|
10296
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10297
|
+
status,
|
|
10298
|
+
message,
|
|
10299
|
+
data
|
|
10300
|
+
});
|
|
10301
|
+
}
|
|
10302
|
+
async checkViaDkimViewMethod(rec) {
|
|
10098
10303
|
const { dkimVerifierAccountId, verificationViewMethod } = this.getConfig();
|
|
10099
10304
|
if (!dkimVerifierAccountId) return null;
|
|
10100
10305
|
try {
|
|
10101
|
-
const
|
|
10102
|
-
|
|
10103
|
-
method: verificationViewMethod,
|
|
10104
|
-
args: { request_id: rec.requestId }
|
|
10105
|
-
});
|
|
10306
|
+
const { getEmailRecoveryVerificationResult } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10307
|
+
const result = await getEmailRecoveryVerificationResult(this.context.nearClient, dkimVerifierAccountId, verificationViewMethod, rec.requestId);
|
|
10106
10308
|
if (!result) return {
|
|
10107
10309
|
completed: false,
|
|
10108
10310
|
success: false
|
|
@@ -10134,43 +10336,78 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10134
10336
|
transactionHash: result.transaction_hash
|
|
10135
10337
|
};
|
|
10136
10338
|
} catch (err$1) {
|
|
10137
|
-
console.warn("[EmailRecoveryFlow] get_verification_result view failed;
|
|
10339
|
+
console.warn("[EmailRecoveryFlow] get_verification_result view failed; will retry", err$1);
|
|
10138
10340
|
return null;
|
|
10139
10341
|
}
|
|
10140
10342
|
}
|
|
10141
|
-
|
|
10142
|
-
|
|
10143
|
-
|
|
10144
|
-
|
|
10145
|
-
|
|
10146
|
-
|
|
10147
|
-
|
|
10148
|
-
|
|
10149
|
-
|
|
10150
|
-
|
|
10151
|
-
|
|
10152
|
-
|
|
10153
|
-
|
|
10154
|
-
|
|
10155
|
-
|
|
10156
|
-
|
|
10157
|
-
|
|
10343
|
+
buildPollingEventData(rec, details) {
|
|
10344
|
+
return {
|
|
10345
|
+
accountId: rec.accountId,
|
|
10346
|
+
requestId: rec.requestId,
|
|
10347
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10348
|
+
transactionHash: details.transactionHash,
|
|
10349
|
+
elapsedMs: details.elapsedMs,
|
|
10350
|
+
pollCount: details.pollCount
|
|
10351
|
+
};
|
|
10352
|
+
}
|
|
10353
|
+
async sleepForPollInterval(ms) {
|
|
10354
|
+
await new Promise((resolve) => {
|
|
10355
|
+
this.pollIntervalResolver = resolve;
|
|
10356
|
+
this.pollingTimer = setTimeout(() => {
|
|
10357
|
+
this.pollIntervalResolver = void 0;
|
|
10358
|
+
this.pollingTimer = void 0;
|
|
10359
|
+
resolve();
|
|
10360
|
+
}, ms);
|
|
10361
|
+
}).finally(() => {
|
|
10362
|
+
this.pollIntervalResolver = void 0;
|
|
10363
|
+
});
|
|
10364
|
+
}
|
|
10365
|
+
async pollUntil(args) {
|
|
10366
|
+
const now = args.now ?? Date.now;
|
|
10367
|
+
const sleep = args.sleep ?? this.sleepForPollInterval.bind(this);
|
|
10368
|
+
const startedAt = now();
|
|
10369
|
+
let pollCount = 0;
|
|
10370
|
+
while (!args.isCancelled()) {
|
|
10371
|
+
pollCount += 1;
|
|
10372
|
+
const elapsedMs$1 = now() - startedAt;
|
|
10373
|
+
if (elapsedMs$1 > args.timeoutMs) return {
|
|
10374
|
+
status: "timedOut",
|
|
10375
|
+
elapsedMs: elapsedMs$1,
|
|
10376
|
+
pollCount
|
|
10377
|
+
};
|
|
10378
|
+
const result = await args.tick({
|
|
10379
|
+
elapsedMs: elapsedMs$1,
|
|
10380
|
+
pollCount
|
|
10381
|
+
});
|
|
10382
|
+
if (result.done) return {
|
|
10383
|
+
status: "completed",
|
|
10384
|
+
value: result.value,
|
|
10385
|
+
elapsedMs: elapsedMs$1,
|
|
10386
|
+
pollCount
|
|
10387
|
+
};
|
|
10388
|
+
if (args.isCancelled()) return {
|
|
10389
|
+
status: "cancelled",
|
|
10390
|
+
elapsedMs: elapsedMs$1,
|
|
10391
|
+
pollCount
|
|
10392
|
+
};
|
|
10393
|
+
await sleep(args.intervalMs);
|
|
10158
10394
|
}
|
|
10159
|
-
|
|
10160
|
-
return
|
|
10395
|
+
const elapsedMs = now() - startedAt;
|
|
10396
|
+
return {
|
|
10397
|
+
status: "cancelled",
|
|
10398
|
+
elapsedMs,
|
|
10399
|
+
pollCount
|
|
10400
|
+
};
|
|
10401
|
+
}
|
|
10402
|
+
async loadPending(accountId, nearPublicKey) {
|
|
10403
|
+
return this.pendingStore.get(accountId, nearPublicKey);
|
|
10161
10404
|
}
|
|
10162
10405
|
async savePending(rec) {
|
|
10163
|
-
|
|
10164
|
-
await IndexedDBManager.clientDB.setAppState(key, rec);
|
|
10165
|
-
await IndexedDBManager.clientDB.setAppState(this.getPendingIndexKey(rec.accountId), rec.nearPublicKey).catch(() => {});
|
|
10406
|
+
await this.pendingStore.set(rec);
|
|
10166
10407
|
this.pending = rec;
|
|
10167
10408
|
}
|
|
10168
10409
|
async clearPending(accountId, nearPublicKey) {
|
|
10169
|
-
|
|
10170
|
-
const idx = await IndexedDBManager.clientDB.getAppState(indexKey).catch(() => void 0);
|
|
10171
|
-
const resolvedNearPublicKey = nearPublicKey || idx || "";
|
|
10172
|
-
if (resolvedNearPublicKey) await IndexedDBManager.clientDB.setAppState(this.getPendingRecordKey(accountId, resolvedNearPublicKey), void 0).catch(() => {});
|
|
10173
|
-
if (!nearPublicKey || idx === nearPublicKey) await IndexedDBManager.clientDB.setAppState(indexKey, void 0).catch(() => {});
|
|
10410
|
+
await this.pendingStore.clear(accountId, nearPublicKey);
|
|
10174
10411
|
if (this.pending && this.pending.accountId === accountId && (!nearPublicKey || this.pending.nearPublicKey === nearPublicKey)) this.pending = null;
|
|
10175
10412
|
}
|
|
10176
10413
|
getState() {
|
|
@@ -10184,48 +10421,14 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10184
10421
|
const { accountId, nearPublicKey } = args;
|
|
10185
10422
|
this.cancelled = false;
|
|
10186
10423
|
this.error = void 0;
|
|
10187
|
-
const
|
|
10188
|
-
|
|
10189
|
-
|
|
10190
|
-
|
|
10191
|
-
|
|
10192
|
-
|
|
10193
|
-
const nearAccountId = toAccountId(accountId);
|
|
10194
|
-
let rec = this.pending;
|
|
10195
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10196
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10197
|
-
this.pending = rec;
|
|
10198
|
-
}
|
|
10199
|
-
if (!rec) {
|
|
10200
|
-
const err$1 = this.emitError(3, "No pending email recovery record found for this account");
|
|
10201
|
-
await this.options?.afterCall?.(false);
|
|
10202
|
-
throw err$1;
|
|
10203
|
-
}
|
|
10204
|
-
if (rec.status === "error") {
|
|
10205
|
-
const err$1 = this.emitError(3, "Pending email recovery is in an error state; please restart the flow");
|
|
10206
|
-
await this.options?.afterCall?.(false);
|
|
10207
|
-
throw err$1;
|
|
10208
|
-
}
|
|
10209
|
-
if (rec.status === "finalizing" || rec.status === "complete") {
|
|
10210
|
-
const err$1 = this.emitError(3, "Recovery email has already been processed on-chain for this request");
|
|
10211
|
-
await this.options?.afterCall?.(false);
|
|
10212
|
-
throw err$1;
|
|
10213
|
-
}
|
|
10424
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(3, accountId);
|
|
10425
|
+
const rec = await this.resolvePendingOrFail(3, {
|
|
10426
|
+
accountId: nearAccountId,
|
|
10427
|
+
nearPublicKey
|
|
10428
|
+
}, { allowErrorStatus: false });
|
|
10429
|
+
if (rec.status === "finalizing" || rec.status === "complete") await this.fail(3, "Recovery email has already been processed on-chain for this request");
|
|
10214
10430
|
const mailtoUrl = rec.status === "awaiting-email" ? await this.buildMailtoUrlAndUpdateStatus(rec) : this.buildMailtoUrlInternal(rec);
|
|
10215
|
-
this.
|
|
10216
|
-
this.emit({
|
|
10217
|
-
step: 3,
|
|
10218
|
-
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10219
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10220
|
-
message: "New device key created; please send the recovery email from your registered address.",
|
|
10221
|
-
data: {
|
|
10222
|
-
accountId: rec.accountId,
|
|
10223
|
-
recoveryEmail: rec.recoveryEmail,
|
|
10224
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10225
|
-
requestId: rec.requestId,
|
|
10226
|
-
mailtoUrl
|
|
10227
|
-
}
|
|
10228
|
-
});
|
|
10431
|
+
this.emitAwaitEmail(rec, mailtoUrl);
|
|
10229
10432
|
await this.options?.afterCall?.(true, void 0);
|
|
10230
10433
|
return mailtoUrl;
|
|
10231
10434
|
}
|
|
@@ -10240,49 +10443,10 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10240
10443
|
status: EmailRecoveryStatus.PROGRESS,
|
|
10241
10444
|
message: "Preparing email recovery..."
|
|
10242
10445
|
});
|
|
10243
|
-
const
|
|
10244
|
-
|
|
10245
|
-
|
|
10246
|
-
|
|
10247
|
-
throw err$1;
|
|
10248
|
-
}
|
|
10249
|
-
const nearAccountId = toAccountId(accountId);
|
|
10250
|
-
const { minBalanceYocto } = this.getConfig();
|
|
10251
|
-
const STORAGE_PRICE_PER_BYTE = BigInt("10000000000000000000");
|
|
10252
|
-
try {
|
|
10253
|
-
const accountView = await this.context.nearClient.viewAccount(nearAccountId);
|
|
10254
|
-
const amount = BigInt(accountView.amount || "0");
|
|
10255
|
-
const locked = BigInt(accountView.locked || "0");
|
|
10256
|
-
const storageUsage = BigInt(accountView.storage_usage || 0);
|
|
10257
|
-
const storageCost = storageUsage * STORAGE_PRICE_PER_BYTE;
|
|
10258
|
-
const rawAvailable = amount - locked - storageCost;
|
|
10259
|
-
const available = rawAvailable > 0 ? rawAvailable : BigInt(0);
|
|
10260
|
-
if (available < BigInt(minBalanceYocto)) {
|
|
10261
|
-
const err$1 = this.emitError(1, `This account does not have enough NEAR to finalize recovery. Available: ${available.toString()} yocto; required: ${String(minBalanceYocto)}. Please top up and try again.`);
|
|
10262
|
-
await this.options?.afterCall?.(false);
|
|
10263
|
-
throw err$1;
|
|
10264
|
-
}
|
|
10265
|
-
} catch (e) {
|
|
10266
|
-
const err$1 = this.emitError(1, e?.message || "Failed to fetch account balance for recovery");
|
|
10267
|
-
await this.options?.afterCall?.(false);
|
|
10268
|
-
throw err$1;
|
|
10269
|
-
}
|
|
10270
|
-
const canonicalEmail = String(recoveryEmail || "").trim().toLowerCase();
|
|
10271
|
-
if (!canonicalEmail) {
|
|
10272
|
-
const err$1 = this.emitError(1, "Recovery email is required for email-based account recovery");
|
|
10273
|
-
await this.options?.afterCall?.(false);
|
|
10274
|
-
throw err$1;
|
|
10275
|
-
}
|
|
10276
|
-
let deviceNumber = 1;
|
|
10277
|
-
try {
|
|
10278
|
-
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
|
|
10279
|
-
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, nearAccountId);
|
|
10280
|
-
const numbers = authenticators.map((a) => a?.authenticator?.deviceNumber).filter((n) => typeof n === "number" && Number.isFinite(n));
|
|
10281
|
-
const max = numbers.length > 0 ? Math.max(...numbers) : 0;
|
|
10282
|
-
deviceNumber = max + 1;
|
|
10283
|
-
} catch {
|
|
10284
|
-
deviceNumber = 1;
|
|
10285
|
-
}
|
|
10446
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(1, accountId);
|
|
10447
|
+
await this.assertSufficientBalance(nearAccountId);
|
|
10448
|
+
const canonicalEmail = await this.getCanonicalRecoveryEmailOrFail(recoveryEmail);
|
|
10449
|
+
const deviceNumber = await this.getNextDeviceNumberFromContract(nearAccountId);
|
|
10286
10450
|
this.phase = EmailRecoveryPhase.STEP_2_TOUCH_ID_REGISTRATION;
|
|
10287
10451
|
this.emit({
|
|
10288
10452
|
step: 2,
|
|
@@ -10291,69 +10455,24 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10291
10455
|
message: "Collecting passkey for email recovery..."
|
|
10292
10456
|
});
|
|
10293
10457
|
try {
|
|
10294
|
-
const
|
|
10295
|
-
|
|
10296
|
-
body: this.options?.confirmerText?.body ?? "Create a recovery account and send an encrypted email to recover your account."
|
|
10297
|
-
};
|
|
10298
|
-
const confirm = await this.context.webAuthnManager.requestRegistrationCredentialConfirmation({
|
|
10299
|
-
nearAccountId,
|
|
10300
|
-
deviceNumber,
|
|
10301
|
-
confirmerText,
|
|
10302
|
-
confirmationConfigOverride: this.options?.confirmationConfig
|
|
10303
|
-
});
|
|
10304
|
-
if (!confirm.confirmed || !confirm.credential) {
|
|
10305
|
-
const err$1 = this.emitError(2, "User cancelled email recovery TouchID confirmation");
|
|
10306
|
-
await this.options?.afterCall?.(false);
|
|
10307
|
-
throw err$1;
|
|
10308
|
-
}
|
|
10309
|
-
const vrfDerivationResult = await this.context.webAuthnManager.deriveVrfKeypair({
|
|
10310
|
-
credential: confirm.credential,
|
|
10311
|
-
nearAccountId
|
|
10312
|
-
});
|
|
10313
|
-
if (!vrfDerivationResult.success || !vrfDerivationResult.encryptedVrfKeypair) {
|
|
10314
|
-
const err$1 = this.emitError(2, "Failed to derive VRF keypair from PRF for email recovery");
|
|
10315
|
-
await this.options?.afterCall?.(false);
|
|
10316
|
-
throw err$1;
|
|
10317
|
-
}
|
|
10318
|
-
const nearKeyResult = await this.context.webAuthnManager.deriveNearKeypairAndEncryptFromSerialized({
|
|
10319
|
-
nearAccountId,
|
|
10320
|
-
credential: confirm.credential,
|
|
10321
|
-
options: { deviceNumber }
|
|
10322
|
-
});
|
|
10323
|
-
if (!nearKeyResult.success || !nearKeyResult.publicKey) {
|
|
10324
|
-
const err$1 = this.emitError(2, "Failed to derive NEAR keypair for email recovery");
|
|
10325
|
-
await this.options?.afterCall?.(false);
|
|
10326
|
-
throw err$1;
|
|
10327
|
-
}
|
|
10458
|
+
const confirm = await this.collectRecoveryCredentialOrFail(nearAccountId, deviceNumber);
|
|
10459
|
+
const derivedKeys = await this.deriveRecoveryKeysOrFail(nearAccountId, deviceNumber, confirm.credential);
|
|
10328
10460
|
const rec = {
|
|
10329
10461
|
accountId: nearAccountId,
|
|
10330
10462
|
recoveryEmail: canonicalEmail,
|
|
10331
10463
|
deviceNumber,
|
|
10332
|
-
nearPublicKey:
|
|
10464
|
+
nearPublicKey: derivedKeys.nearPublicKey,
|
|
10333
10465
|
requestId: generateEmailRecoveryRequestId(),
|
|
10334
|
-
encryptedVrfKeypair:
|
|
10335
|
-
serverEncryptedVrfKeypair:
|
|
10336
|
-
vrfPublicKey:
|
|
10466
|
+
encryptedVrfKeypair: derivedKeys.encryptedVrfKeypair,
|
|
10467
|
+
serverEncryptedVrfKeypair: derivedKeys.serverEncryptedVrfKeypair,
|
|
10468
|
+
vrfPublicKey: derivedKeys.vrfPublicKey,
|
|
10337
10469
|
credential: confirm.credential,
|
|
10338
10470
|
vrfChallenge: confirm.vrfChallenge || void 0,
|
|
10339
10471
|
createdAt: Date.now(),
|
|
10340
10472
|
status: "awaiting-email"
|
|
10341
10473
|
};
|
|
10342
10474
|
const mailtoUrl = await this.buildMailtoUrlAndUpdateStatus(rec);
|
|
10343
|
-
this.
|
|
10344
|
-
this.emit({
|
|
10345
|
-
step: 3,
|
|
10346
|
-
phase: EmailRecoveryPhase.STEP_3_AWAIT_EMAIL,
|
|
10347
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10348
|
-
message: "New device key created; please send the recovery email from your registered address.",
|
|
10349
|
-
data: {
|
|
10350
|
-
accountId: rec.accountId,
|
|
10351
|
-
recoveryEmail: rec.recoveryEmail,
|
|
10352
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10353
|
-
requestId: rec.requestId,
|
|
10354
|
-
mailtoUrl
|
|
10355
|
-
}
|
|
10356
|
-
});
|
|
10475
|
+
this.emitAwaitEmail(rec, mailtoUrl);
|
|
10357
10476
|
await this.options?.afterCall?.(true, void 0);
|
|
10358
10477
|
return {
|
|
10359
10478
|
mailtoUrl,
|
|
@@ -10381,28 +10500,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10381
10500
|
const { accountId, nearPublicKey } = args;
|
|
10382
10501
|
this.cancelled = false;
|
|
10383
10502
|
this.error = void 0;
|
|
10384
|
-
const
|
|
10385
|
-
|
|
10386
|
-
|
|
10387
|
-
|
|
10388
|
-
|
|
10389
|
-
}
|
|
10390
|
-
const nearAccountId = toAccountId(accountId);
|
|
10391
|
-
let rec = this.pending;
|
|
10392
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10393
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10394
|
-
this.pending = rec;
|
|
10395
|
-
}
|
|
10396
|
-
if (!rec) {
|
|
10397
|
-
const err$1 = this.emitError(4, "No pending email recovery record found for this account");
|
|
10398
|
-
await this.options?.afterCall?.(false);
|
|
10399
|
-
throw err$1;
|
|
10400
|
-
}
|
|
10401
|
-
if (rec.status === "error") {
|
|
10402
|
-
const err$1 = this.emitError(4, "Pending email recovery is in an error state; please restart the flow");
|
|
10403
|
-
await this.options?.afterCall?.(false);
|
|
10404
|
-
throw err$1;
|
|
10405
|
-
}
|
|
10503
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
|
|
10504
|
+
const rec = await this.resolvePendingOrFail(4, {
|
|
10505
|
+
accountId: nearAccountId,
|
|
10506
|
+
nearPublicKey
|
|
10507
|
+
}, { allowErrorStatus: false });
|
|
10406
10508
|
if (rec.status === "complete" || rec.status === "finalizing") {
|
|
10407
10509
|
await this.options?.afterCall?.(true, void 0);
|
|
10408
10510
|
return;
|
|
@@ -10442,23 +10544,11 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10442
10544
|
const { accountId, nearPublicKey } = args;
|
|
10443
10545
|
this.cancelled = false;
|
|
10444
10546
|
this.error = void 0;
|
|
10445
|
-
const
|
|
10446
|
-
|
|
10447
|
-
|
|
10448
|
-
|
|
10449
|
-
|
|
10450
|
-
}
|
|
10451
|
-
const nearAccountId = toAccountId(accountId);
|
|
10452
|
-
let rec = this.pending;
|
|
10453
|
-
if (!rec || rec.accountId !== nearAccountId || nearPublicKey && rec.nearPublicKey !== nearPublicKey) {
|
|
10454
|
-
rec = await this.loadPending(nearAccountId, nearPublicKey);
|
|
10455
|
-
this.pending = rec;
|
|
10456
|
-
}
|
|
10457
|
-
if (!rec) {
|
|
10458
|
-
const err$1 = this.emitError(4, "No pending email recovery record found for this account");
|
|
10459
|
-
await this.options?.afterCall?.(false);
|
|
10460
|
-
throw err$1;
|
|
10461
|
-
}
|
|
10547
|
+
const nearAccountId = await this.assertValidAccountIdOrFail(4, accountId);
|
|
10548
|
+
const rec = await this.resolvePendingOrFail(4, {
|
|
10549
|
+
accountId: nearAccountId,
|
|
10550
|
+
nearPublicKey
|
|
10551
|
+
}, { allowErrorStatus: true });
|
|
10462
10552
|
this.emit({
|
|
10463
10553
|
step: 0,
|
|
10464
10554
|
phase: EmailRecoveryPhase.RESUMED_FROM_PENDING,
|
|
@@ -10494,245 +10584,242 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10494
10584
|
}
|
|
10495
10585
|
this.phase = EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT;
|
|
10496
10586
|
this.pollingStartedAt = Date.now();
|
|
10497
|
-
|
|
10498
|
-
|
|
10499
|
-
|
|
10500
|
-
|
|
10501
|
-
|
|
10502
|
-
const
|
|
10587
|
+
const pollResult = await this.pollUntil({
|
|
10588
|
+
intervalMs: pollingIntervalMs,
|
|
10589
|
+
timeoutMs: maxPollingDurationMs,
|
|
10590
|
+
isCancelled: () => this.cancelled,
|
|
10591
|
+
tick: async ({ elapsedMs, pollCount }) => {
|
|
10592
|
+
const verification = await this.checkViaDkimViewMethod(rec);
|
|
10593
|
+
const completed = verification?.completed === true;
|
|
10594
|
+
const success = verification?.success === true;
|
|
10595
|
+
this.emit({
|
|
10596
|
+
step: 4,
|
|
10597
|
+
phase: EmailRecoveryPhase.STEP_4_POLLING_VERIFICATION_RESULT,
|
|
10598
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10599
|
+
message: completed && success ? `Email verified for request ${rec.requestId}; finalizing registration` : `Waiting for email verification for request ${rec.requestId}`,
|
|
10600
|
+
data: this.buildPollingEventData(rec, {
|
|
10601
|
+
transactionHash: verification?.transactionHash,
|
|
10602
|
+
elapsedMs,
|
|
10603
|
+
pollCount
|
|
10604
|
+
})
|
|
10605
|
+
});
|
|
10606
|
+
if (!completed) return { done: false };
|
|
10607
|
+
if (!success) return {
|
|
10608
|
+
done: true,
|
|
10609
|
+
value: {
|
|
10610
|
+
outcome: "failed",
|
|
10611
|
+
errorMessage: verification?.errorMessage || "Email verification failed"
|
|
10612
|
+
}
|
|
10613
|
+
};
|
|
10614
|
+
return {
|
|
10615
|
+
done: true,
|
|
10616
|
+
value: { outcome: "verified" }
|
|
10617
|
+
};
|
|
10618
|
+
}
|
|
10619
|
+
});
|
|
10620
|
+
if (pollResult.status === "completed") {
|
|
10621
|
+
if (pollResult.value.outcome === "failed") {
|
|
10622
|
+
const err$2 = this.emitError(4, pollResult.value.errorMessage);
|
|
10503
10623
|
rec.status = "error";
|
|
10504
10624
|
await this.savePending(rec);
|
|
10505
10625
|
await this.options?.afterCall?.(false);
|
|
10506
10626
|
throw err$2;
|
|
10507
10627
|
}
|
|
10508
|
-
|
|
10509
|
-
|
|
10510
|
-
|
|
10511
|
-
|
|
10512
|
-
|
|
10513
|
-
|
|
10514
|
-
|
|
10515
|
-
|
|
10516
|
-
|
|
10517
|
-
|
|
10518
|
-
requestId: rec.requestId,
|
|
10519
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10520
|
-
transactionHash: verification?.transactionHash,
|
|
10521
|
-
elapsedMs: elapsed,
|
|
10522
|
-
pollCount
|
|
10523
|
-
}
|
|
10524
|
-
});
|
|
10525
|
-
if (completed) {
|
|
10526
|
-
if (!success) {
|
|
10527
|
-
const err$2 = this.emitError(4, verification?.errorMessage || "Email verification failed");
|
|
10528
|
-
rec.status = "error";
|
|
10529
|
-
await this.savePending(rec);
|
|
10530
|
-
await this.options?.afterCall?.(false);
|
|
10531
|
-
throw err$2;
|
|
10532
|
-
}
|
|
10533
|
-
rec.status = "finalizing";
|
|
10534
|
-
await this.savePending(rec);
|
|
10535
|
-
return;
|
|
10536
|
-
}
|
|
10537
|
-
if (this.cancelled) break;
|
|
10538
|
-
await new Promise((resolve) => {
|
|
10539
|
-
this.pollIntervalResolver = resolve;
|
|
10540
|
-
this.pollingTimer = setTimeout(() => {
|
|
10541
|
-
this.pollIntervalResolver = void 0;
|
|
10542
|
-
this.pollingTimer = void 0;
|
|
10543
|
-
resolve();
|
|
10544
|
-
}, pollingIntervalMs);
|
|
10545
|
-
}).finally(() => {
|
|
10546
|
-
this.pollIntervalResolver = void 0;
|
|
10547
|
-
});
|
|
10628
|
+
rec.status = "finalizing";
|
|
10629
|
+
await this.savePending(rec);
|
|
10630
|
+
return;
|
|
10631
|
+
}
|
|
10632
|
+
if (pollResult.status === "timedOut") {
|
|
10633
|
+
const err$2 = this.emitError(4, "Timed out waiting for recovery email to be processed on-chain");
|
|
10634
|
+
rec.status = "error";
|
|
10635
|
+
await this.savePending(rec);
|
|
10636
|
+
await this.options?.afterCall?.(false);
|
|
10637
|
+
throw err$2;
|
|
10548
10638
|
}
|
|
10549
10639
|
const err$1 = this.emitError(4, "Email recovery polling was cancelled");
|
|
10550
10640
|
await this.options?.afterCall?.(false);
|
|
10551
10641
|
throw err$1;
|
|
10552
10642
|
}
|
|
10553
|
-
|
|
10554
|
-
this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
|
|
10555
|
-
this.emit({
|
|
10556
|
-
step: 5,
|
|
10557
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10558
|
-
status: EmailRecoveryStatus.PROGRESS,
|
|
10559
|
-
message: "Finalizing email recovery registration...",
|
|
10560
|
-
data: {
|
|
10561
|
-
accountId: rec.accountId,
|
|
10562
|
-
nearPublicKey: rec.nearPublicKey
|
|
10563
|
-
}
|
|
10564
|
-
});
|
|
10643
|
+
initializeNonceManager(rec) {
|
|
10565
10644
|
const nonceManager = this.context.webAuthnManager.getNonceManager();
|
|
10566
10645
|
const accountId = toAccountId(rec.accountId);
|
|
10567
10646
|
nonceManager.initializeUser(accountId, rec.nearPublicKey);
|
|
10647
|
+
return {
|
|
10648
|
+
nonceManager,
|
|
10649
|
+
accountId
|
|
10650
|
+
};
|
|
10651
|
+
}
|
|
10652
|
+
async signRegistrationTx(rec, accountId) {
|
|
10653
|
+
const vrfChallenge = rec.vrfChallenge;
|
|
10654
|
+
if (!vrfChallenge) return this.fail(5, "Missing VRF challenge for email recovery registration");
|
|
10655
|
+
const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
|
|
10656
|
+
nearAccountId: accountId,
|
|
10657
|
+
credential: rec.credential,
|
|
10658
|
+
vrfChallenge,
|
|
10659
|
+
deterministicVrfPublicKey: rec.vrfPublicKey,
|
|
10660
|
+
deviceNumber: rec.deviceNumber
|
|
10661
|
+
});
|
|
10662
|
+
if (!registrationResult.success || !registrationResult.signedTransaction) await this.fail(5, registrationResult.error || "Failed to sign email recovery registration transaction");
|
|
10663
|
+
return registrationResult.signedTransaction;
|
|
10664
|
+
}
|
|
10665
|
+
async broadcastRegistrationTxAndWaitFinal(rec, signedTx) {
|
|
10568
10666
|
try {
|
|
10569
|
-
|
|
10570
|
-
const err$1 = this.emitError(5, "Missing VRF challenge for email recovery registration");
|
|
10571
|
-
await this.options?.afterCall?.(false);
|
|
10572
|
-
throw err$1;
|
|
10573
|
-
}
|
|
10574
|
-
const registrationResult = await this.context.webAuthnManager.signDevice2RegistrationWithStoredKey({
|
|
10575
|
-
nearAccountId: accountId,
|
|
10576
|
-
credential: rec.credential,
|
|
10577
|
-
vrfChallenge: rec.vrfChallenge,
|
|
10578
|
-
deterministicVrfPublicKey: rec.vrfPublicKey,
|
|
10579
|
-
deviceNumber: rec.deviceNumber
|
|
10580
|
-
});
|
|
10581
|
-
if (!registrationResult.success || !registrationResult.signedTransaction) {
|
|
10582
|
-
const err$1 = this.emitError(5, registrationResult.error || "Failed to sign email recovery registration transaction");
|
|
10583
|
-
await this.options?.afterCall?.(false);
|
|
10584
|
-
throw err$1;
|
|
10585
|
-
}
|
|
10586
|
-
const signedTx = registrationResult.signedTransaction;
|
|
10667
|
+
const txResult = await this.context.nearClient.sendTransaction(signedTx, DEFAULT_WAIT_STATUS.linkDeviceRegistration);
|
|
10587
10668
|
try {
|
|
10588
|
-
const
|
|
10589
|
-
|
|
10590
|
-
|
|
10591
|
-
|
|
10592
|
-
|
|
10593
|
-
|
|
10594
|
-
|
|
10595
|
-
|
|
10596
|
-
|
|
10597
|
-
|
|
10598
|
-
accountId: rec.accountId,
|
|
10599
|
-
nearPublicKey: rec.nearPublicKey,
|
|
10600
|
-
transactionHash: txHash
|
|
10601
|
-
}
|
|
10602
|
-
});
|
|
10603
|
-
try {
|
|
10604
|
-
await IndexedDBManager.clientDB.storeWebAuthnUserData({
|
|
10605
|
-
nearAccountId: accountId,
|
|
10606
|
-
deviceNumber: rec.deviceNumber,
|
|
10607
|
-
clientNearPublicKey: rec.nearPublicKey,
|
|
10608
|
-
passkeyCredential: {
|
|
10609
|
-
id: rec.credential.id,
|
|
10610
|
-
rawId: rec.credential.rawId
|
|
10611
|
-
},
|
|
10612
|
-
encryptedVrfKeypair: rec.encryptedVrfKeypair,
|
|
10613
|
-
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10614
|
-
});
|
|
10615
|
-
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-DEv9x5-f.js");
|
|
10616
|
-
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
|
|
10617
|
-
const mappedAuthenticators = authenticators.map(({ authenticator }) => ({
|
|
10618
|
-
credentialId: authenticator.credentialId,
|
|
10619
|
-
credentialPublicKey: authenticator.credentialPublicKey,
|
|
10620
|
-
transports: authenticator.transports,
|
|
10621
|
-
name: authenticator.name,
|
|
10622
|
-
registered: authenticator.registered.toISOString(),
|
|
10623
|
-
vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
|
|
10624
|
-
deviceNumber: authenticator.deviceNumber
|
|
10625
|
-
}));
|
|
10626
|
-
await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
|
|
10627
|
-
await IndexedDBManager.clientDB.setLastUser(accountId, rec.deviceNumber);
|
|
10628
|
-
} catch (syncErr) {
|
|
10629
|
-
console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", syncErr);
|
|
10630
|
-
}
|
|
10669
|
+
const txHash = txResult?.transaction?.hash || txResult?.transaction_hash;
|
|
10670
|
+
if (txHash) this.emit({
|
|
10671
|
+
step: 5,
|
|
10672
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10673
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10674
|
+
message: "Registration transaction confirmed",
|
|
10675
|
+
data: {
|
|
10676
|
+
accountId: rec.accountId,
|
|
10677
|
+
nearPublicKey: rec.nearPublicKey,
|
|
10678
|
+
transactionHash: txHash
|
|
10631
10679
|
}
|
|
10632
|
-
}
|
|
10633
|
-
|
|
10634
|
-
const msg = String(e?.message || "");
|
|
10635
|
-
const err$1 = this.emitError(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
|
|
10636
|
-
await this.options?.afterCall?.(false);
|
|
10637
|
-
throw err$1;
|
|
10638
|
-
}
|
|
10639
|
-
try {
|
|
10640
|
-
const txNonce = signedTx.transaction?.nonce;
|
|
10641
|
-
if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
|
|
10680
|
+
});
|
|
10681
|
+
return txHash;
|
|
10642
10682
|
} catch {}
|
|
10643
|
-
|
|
10644
|
-
|
|
10683
|
+
} catch (e) {
|
|
10684
|
+
const msg = String(e?.message || "");
|
|
10685
|
+
await this.fail(5, msg || "Failed to broadcast email recovery registration transaction (insufficient funds or RPC error)");
|
|
10686
|
+
}
|
|
10687
|
+
return void 0;
|
|
10688
|
+
}
|
|
10689
|
+
async persistRecoveredUserRecordBestEffort(rec, accountId) {
|
|
10690
|
+
try {
|
|
10691
|
+
await IndexedDBManager.clientDB.storeWebAuthnUserData({
|
|
10645
10692
|
nearAccountId: accountId,
|
|
10646
10693
|
deviceNumber: rec.deviceNumber,
|
|
10647
10694
|
clientNearPublicKey: rec.nearPublicKey,
|
|
10648
|
-
lastUpdated: Date.now(),
|
|
10649
10695
|
passkeyCredential: {
|
|
10650
10696
|
id: rec.credential.id,
|
|
10651
10697
|
rawId: rec.credential.rawId
|
|
10652
10698
|
},
|
|
10653
|
-
encryptedVrfKeypair:
|
|
10654
|
-
encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
|
|
10655
|
-
chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
|
|
10656
|
-
},
|
|
10699
|
+
encryptedVrfKeypair: rec.encryptedVrfKeypair,
|
|
10657
10700
|
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10658
10701
|
});
|
|
10659
|
-
|
|
10660
|
-
|
|
10661
|
-
|
|
10662
|
-
|
|
10663
|
-
nearAccountId: accountId,
|
|
10664
|
-
deviceNumber: rec.deviceNumber,
|
|
10665
|
-
credentialId: rec.credential.rawId,
|
|
10666
|
-
credentialPublicKey,
|
|
10667
|
-
transports: ["internal"],
|
|
10668
|
-
name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
|
|
10669
|
-
registered: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10670
|
-
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10671
|
-
vrfPublicKey: rec.vrfPublicKey
|
|
10672
|
-
});
|
|
10673
|
-
} catch {}
|
|
10674
|
-
await this.attemptAutoLogin(rec);
|
|
10675
|
-
rec.status = "complete";
|
|
10676
|
-
await this.savePending(rec);
|
|
10677
|
-
await this.clearPending(rec.accountId, rec.nearPublicKey);
|
|
10678
|
-
this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
|
|
10679
|
-
this.emit({
|
|
10680
|
-
step: 6,
|
|
10681
|
-
phase: EmailRecoveryPhase.STEP_6_COMPLETE,
|
|
10682
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10683
|
-
message: "Email recovery completed successfully",
|
|
10684
|
-
data: {
|
|
10685
|
-
accountId: rec.accountId,
|
|
10686
|
-
nearPublicKey: rec.nearPublicKey
|
|
10687
|
-
}
|
|
10688
|
-
});
|
|
10689
|
-
} catch (e) {
|
|
10690
|
-
const err$1 = this.emitError(5, e?.message || "Email recovery finalization failed");
|
|
10691
|
-
await this.options?.afterCall?.(false);
|
|
10692
|
-
throw err$1;
|
|
10702
|
+
return true;
|
|
10703
|
+
} catch (err$1) {
|
|
10704
|
+
console.warn("[EmailRecoveryFlow] Failed to store recovery user record:", err$1);
|
|
10705
|
+
return false;
|
|
10693
10706
|
}
|
|
10694
10707
|
}
|
|
10695
|
-
|
|
10708
|
+
mapAuthenticatorsFromContract(authenticators) {
|
|
10709
|
+
return authenticators.map(({ authenticator }) => ({
|
|
10710
|
+
credentialId: authenticator.credentialId,
|
|
10711
|
+
credentialPublicKey: authenticator.credentialPublicKey,
|
|
10712
|
+
transports: authenticator.transports,
|
|
10713
|
+
name: authenticator.name,
|
|
10714
|
+
registered: authenticator.registered.toISOString(),
|
|
10715
|
+
vrfPublicKey: authenticator.vrfPublicKeys?.[0] || "",
|
|
10716
|
+
deviceNumber: authenticator.deviceNumber
|
|
10717
|
+
}));
|
|
10718
|
+
}
|
|
10719
|
+
async syncAuthenticatorsBestEffort(accountId) {
|
|
10696
10720
|
try {
|
|
10697
|
-
|
|
10698
|
-
|
|
10699
|
-
|
|
10700
|
-
|
|
10701
|
-
|
|
10702
|
-
|
|
10721
|
+
const { syncAuthenticatorsContractCall: syncAuthenticatorsContractCall$1 } = await import("./rpcCalls-BQrJMTdg.js");
|
|
10722
|
+
const authenticators = await syncAuthenticatorsContractCall$1(this.context.nearClient, this.context.configs.contractId, accountId);
|
|
10723
|
+
const mappedAuthenticators = this.mapAuthenticatorsFromContract(authenticators);
|
|
10724
|
+
await IndexedDBManager.clientDB.syncAuthenticatorsFromContract(accountId, mappedAuthenticators);
|
|
10725
|
+
return true;
|
|
10726
|
+
} catch (err$1) {
|
|
10727
|
+
console.warn("[EmailRecoveryFlow] Failed to sync authenticators after recovery:", err$1);
|
|
10728
|
+
return false;
|
|
10729
|
+
}
|
|
10730
|
+
}
|
|
10731
|
+
async setLastUserBestEffort(accountId, deviceNumber) {
|
|
10732
|
+
try {
|
|
10733
|
+
await IndexedDBManager.clientDB.setLastUser(accountId, deviceNumber);
|
|
10734
|
+
return true;
|
|
10735
|
+
} catch (err$1) {
|
|
10736
|
+
console.warn("[EmailRecoveryFlow] Failed to set last user after recovery:", err$1);
|
|
10737
|
+
return false;
|
|
10738
|
+
}
|
|
10739
|
+
}
|
|
10740
|
+
async updateNonceBestEffort(nonceManager, signedTx) {
|
|
10741
|
+
try {
|
|
10742
|
+
const txNonce = signedTx.transaction?.nonce;
|
|
10743
|
+
if (txNonce != null) await nonceManager.updateNonceFromBlockchain(this.context.nearClient, String(txNonce));
|
|
10744
|
+
} catch {}
|
|
10745
|
+
}
|
|
10746
|
+
async persistRecoveredUserData(rec, accountId) {
|
|
10747
|
+
const { webAuthnManager } = this.context;
|
|
10748
|
+
const payload = {
|
|
10749
|
+
nearAccountId: accountId,
|
|
10750
|
+
deviceNumber: rec.deviceNumber,
|
|
10751
|
+
clientNearPublicKey: rec.nearPublicKey,
|
|
10752
|
+
lastUpdated: Date.now(),
|
|
10753
|
+
passkeyCredential: {
|
|
10754
|
+
id: rec.credential.id,
|
|
10755
|
+
rawId: rec.credential.rawId
|
|
10756
|
+
},
|
|
10757
|
+
encryptedVrfKeypair: {
|
|
10758
|
+
encryptedVrfDataB64u: rec.encryptedVrfKeypair.encryptedVrfDataB64u,
|
|
10759
|
+
chacha20NonceB64u: rec.encryptedVrfKeypair.chacha20NonceB64u
|
|
10760
|
+
},
|
|
10761
|
+
serverEncryptedVrfKeypair: rec.serverEncryptedVrfKeypair || void 0
|
|
10762
|
+
};
|
|
10763
|
+
await webAuthnManager.storeUserData(payload);
|
|
10764
|
+
}
|
|
10765
|
+
async persistAuthenticatorBestEffort(rec, accountId) {
|
|
10766
|
+
try {
|
|
10767
|
+
const { webAuthnManager } = this.context;
|
|
10768
|
+
const attestationB64u = rec.credential.response.attestationObject;
|
|
10769
|
+
const credentialPublicKey = await webAuthnManager.extractCosePublicKey(attestationB64u);
|
|
10770
|
+
await webAuthnManager.storeAuthenticator({
|
|
10771
|
+
nearAccountId: accountId,
|
|
10772
|
+
deviceNumber: rec.deviceNumber,
|
|
10773
|
+
credentialId: rec.credential.rawId,
|
|
10774
|
+
credentialPublicKey,
|
|
10775
|
+
transports: ["internal"],
|
|
10776
|
+
name: `Device ${rec.deviceNumber} Passkey for ${rec.accountId.split(".")[0]}`,
|
|
10777
|
+
registered: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10778
|
+
syncedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
10779
|
+
vrfPublicKey: rec.vrfPublicKey
|
|
10703
10780
|
});
|
|
10781
|
+
} catch {}
|
|
10782
|
+
}
|
|
10783
|
+
async markCompleteAndClearPending(rec) {
|
|
10784
|
+
rec.status = "complete";
|
|
10785
|
+
await this.savePending(rec);
|
|
10786
|
+
await this.clearPending(rec.accountId, rec.nearPublicKey);
|
|
10787
|
+
}
|
|
10788
|
+
async assertVrfActiveForAccount(accountId, message) {
|
|
10789
|
+
const vrfStatus = await this.context.webAuthnManager.checkVrfStatus();
|
|
10790
|
+
const vrfActiveForAccount = vrfStatus.active && vrfStatus.nearAccountId && String(vrfStatus.nearAccountId) === String(accountId);
|
|
10791
|
+
if (!vrfActiveForAccount) throw new Error(message);
|
|
10792
|
+
}
|
|
10793
|
+
async finalizeLocalLoginState(accountId, deviceNumber) {
|
|
10794
|
+
const { webAuthnManager } = this.context;
|
|
10795
|
+
await webAuthnManager.setLastUser(accountId, deviceNumber);
|
|
10796
|
+
await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
|
|
10797
|
+
try {
|
|
10798
|
+
await getLoginSession(this.context, accountId);
|
|
10799
|
+
} catch {}
|
|
10800
|
+
}
|
|
10801
|
+
async tryShamirUnlock(rec, accountId, deviceNumber) {
|
|
10802
|
+
if (!rec.serverEncryptedVrfKeypair || !rec.serverEncryptedVrfKeypair.serverKeyId || !this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) return false;
|
|
10803
|
+
try {
|
|
10804
|
+
const { webAuthnManager } = this.context;
|
|
10805
|
+
const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
|
|
10806
|
+
nearAccountId: accountId,
|
|
10807
|
+
kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
|
|
10808
|
+
ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
|
|
10809
|
+
serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
|
|
10810
|
+
});
|
|
10811
|
+
if (!unlockResult.success) return false;
|
|
10812
|
+
await this.assertVrfActiveForAccount(accountId, "VRF session inactive after Shamir3Pass unlock");
|
|
10813
|
+
await this.finalizeLocalLoginState(accountId, deviceNumber);
|
|
10814
|
+
return true;
|
|
10815
|
+
} catch (err$1) {
|
|
10816
|
+
console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
|
|
10817
|
+
return false;
|
|
10818
|
+
}
|
|
10819
|
+
}
|
|
10820
|
+
async tryTouchIdUnlock(rec, accountId, deviceNumber) {
|
|
10821
|
+
try {
|
|
10704
10822
|
const { webAuthnManager } = this.context;
|
|
10705
|
-
const accountId = toAccountId(rec.accountId);
|
|
10706
|
-
const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
|
|
10707
|
-
if (deviceNumber === null) throw new Error(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
|
|
10708
|
-
if (rec.serverEncryptedVrfKeypair && rec.serverEncryptedVrfKeypair.serverKeyId && this.context.configs.vrfWorkerConfigs?.shamir3pass?.relayServerUrl) try {
|
|
10709
|
-
const unlockResult = await webAuthnManager.shamir3PassDecryptVrfKeypair({
|
|
10710
|
-
nearAccountId: accountId,
|
|
10711
|
-
kek_s_b64u: rec.serverEncryptedVrfKeypair.kek_s_b64u,
|
|
10712
|
-
ciphertextVrfB64u: rec.serverEncryptedVrfKeypair.ciphertextVrfB64u,
|
|
10713
|
-
serverKeyId: rec.serverEncryptedVrfKeypair.serverKeyId
|
|
10714
|
-
});
|
|
10715
|
-
if (unlockResult.success) {
|
|
10716
|
-
const vrfStatus$1 = await webAuthnManager.checkVrfStatus();
|
|
10717
|
-
const vrfActiveForAccount$1 = vrfStatus$1.active && vrfStatus$1.nearAccountId && String(vrfStatus$1.nearAccountId) === String(accountId);
|
|
10718
|
-
if (!vrfActiveForAccount$1) throw new Error("VRF session inactive after Shamir3Pass unlock");
|
|
10719
|
-
await webAuthnManager.setLastUser(accountId, deviceNumber);
|
|
10720
|
-
await webAuthnManager.initializeCurrentUser(accountId, this.context.nearClient);
|
|
10721
|
-
try {
|
|
10722
|
-
await getLoginSession(this.context, accountId);
|
|
10723
|
-
} catch {}
|
|
10724
|
-
this.emit({
|
|
10725
|
-
step: 5,
|
|
10726
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10727
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10728
|
-
message: `Welcome ${accountId}`,
|
|
10729
|
-
data: { autoLogin: "success" }
|
|
10730
|
-
});
|
|
10731
|
-
return;
|
|
10732
|
-
}
|
|
10733
|
-
} catch (err$1) {
|
|
10734
|
-
console.warn("[EmailRecoveryFlow] Shamir 3-pass unlock failed, falling back to TouchID", err$1);
|
|
10735
|
-
}
|
|
10736
10823
|
const authChallenge = createRandomVRFChallenge();
|
|
10737
10824
|
const storedCredentialId = String(rec.credential?.rawId || rec.credential?.id || "").trim();
|
|
10738
10825
|
const credentialIds = storedCredentialId ? [storedCredentialId] : [];
|
|
@@ -10742,43 +10829,108 @@ var init_emailRecovery = __esm({ "src/core/TatchiPasskey/emailRecovery.ts": (()
|
|
|
10742
10829
|
challenge: authChallenge,
|
|
10743
10830
|
credentialIds: credentialIds.length > 0 ? credentialIds : authenticators.map((a) => a.credentialId)
|
|
10744
10831
|
});
|
|
10745
|
-
if (storedCredentialId && authCredential.rawId !== storedCredentialId)
|
|
10832
|
+
if (storedCredentialId && authCredential.rawId !== storedCredentialId) return {
|
|
10833
|
+
success: false,
|
|
10834
|
+
reason: "Wrong passkey selected during recovery auto-login; please use the newly recovered passkey."
|
|
10835
|
+
};
|
|
10746
10836
|
const vrfUnlockResult = await webAuthnManager.unlockVRFKeypair({
|
|
10747
10837
|
nearAccountId: accountId,
|
|
10748
10838
|
encryptedVrfKeypair: rec.encryptedVrfKeypair,
|
|
10749
10839
|
credential: authCredential
|
|
10750
10840
|
});
|
|
10751
|
-
if (!vrfUnlockResult.success)
|
|
10752
|
-
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
await
|
|
10756
|
-
await
|
|
10757
|
-
|
|
10758
|
-
await getLoginSession(this.context, accountId);
|
|
10759
|
-
} catch {}
|
|
10760
|
-
this.emit({
|
|
10761
|
-
step: 5,
|
|
10762
|
-
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10763
|
-
status: EmailRecoveryStatus.SUCCESS,
|
|
10764
|
-
message: `Welcome ${accountId}`,
|
|
10765
|
-
data: { autoLogin: "success" }
|
|
10766
|
-
});
|
|
10841
|
+
if (!vrfUnlockResult.success) return {
|
|
10842
|
+
success: false,
|
|
10843
|
+
reason: vrfUnlockResult.error || "VRF unlock failed during auto-login"
|
|
10844
|
+
};
|
|
10845
|
+
await this.assertVrfActiveForAccount(accountId, "VRF session inactive after TouchID unlock");
|
|
10846
|
+
await this.finalizeLocalLoginState(accountId, deviceNumber);
|
|
10847
|
+
return { success: true };
|
|
10767
10848
|
} catch (err$1) {
|
|
10768
|
-
|
|
10769
|
-
|
|
10770
|
-
|
|
10771
|
-
}
|
|
10849
|
+
return {
|
|
10850
|
+
success: false,
|
|
10851
|
+
reason: err$1?.message || String(err$1)
|
|
10852
|
+
};
|
|
10853
|
+
}
|
|
10854
|
+
}
|
|
10855
|
+
async handleAutoLoginFailure(reason, err$1) {
|
|
10856
|
+
console.warn("[EmailRecoveryFlow] Auto-login failed after recovery", err$1 ?? reason);
|
|
10857
|
+
try {
|
|
10858
|
+
await this.context.webAuthnManager.clearVrfSession();
|
|
10859
|
+
} catch {}
|
|
10860
|
+
return {
|
|
10861
|
+
success: false,
|
|
10862
|
+
reason
|
|
10863
|
+
};
|
|
10864
|
+
}
|
|
10865
|
+
async finalizeRegistration(rec) {
|
|
10866
|
+
this.phase = EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION;
|
|
10867
|
+
this.emit({
|
|
10868
|
+
step: 5,
|
|
10869
|
+
phase: EmailRecoveryPhase.STEP_5_FINALIZING_REGISTRATION,
|
|
10870
|
+
status: EmailRecoveryStatus.PROGRESS,
|
|
10871
|
+
message: "Finalizing email recovery registration...",
|
|
10872
|
+
data: {
|
|
10873
|
+
accountId: rec.accountId,
|
|
10874
|
+
nearPublicKey: rec.nearPublicKey
|
|
10875
|
+
}
|
|
10876
|
+
});
|
|
10877
|
+
try {
|
|
10878
|
+
const { nonceManager, accountId } = this.initializeNonceManager(rec);
|
|
10879
|
+
const signedTx = await this.signRegistrationTx(rec, accountId);
|
|
10880
|
+
const txHash = await this.broadcastRegistrationTxAndWaitFinal(rec, signedTx);
|
|
10881
|
+
if (txHash) {
|
|
10882
|
+
const storedUser = await this.persistRecoveredUserRecordBestEffort(rec, accountId);
|
|
10883
|
+
if (storedUser) {
|
|
10884
|
+
const syncedAuthenticators = await this.syncAuthenticatorsBestEffort(accountId);
|
|
10885
|
+
if (syncedAuthenticators) await this.setLastUserBestEffort(accountId, rec.deviceNumber);
|
|
10886
|
+
}
|
|
10887
|
+
}
|
|
10888
|
+
await this.updateNonceBestEffort(nonceManager, signedTx);
|
|
10889
|
+
await this.persistRecoveredUserData(rec, accountId);
|
|
10890
|
+
await this.persistAuthenticatorBestEffort(rec, accountId);
|
|
10891
|
+
this.emitAutoLoginEvent(EmailRecoveryStatus.PROGRESS, "Attempting auto-login with recovered device...", { autoLogin: "progress" });
|
|
10892
|
+
const autoLoginResult = await this.attemptAutoLogin(rec);
|
|
10893
|
+
if (autoLoginResult.success) this.emitAutoLoginEvent(EmailRecoveryStatus.SUCCESS, `Welcome ${accountId}`, { autoLogin: "success" });
|
|
10894
|
+
else this.emitAutoLoginEvent(EmailRecoveryStatus.ERROR, "Auto-login failed; please log in manually on this device.", {
|
|
10895
|
+
error: autoLoginResult.reason,
|
|
10896
|
+
autoLogin: "error"
|
|
10897
|
+
});
|
|
10898
|
+
await this.markCompleteAndClearPending(rec);
|
|
10899
|
+
this.phase = EmailRecoveryPhase.STEP_6_COMPLETE;
|
|
10772
10900
|
this.emit({
|
|
10773
|
-
step:
|
|
10774
|
-
phase: EmailRecoveryPhase.
|
|
10775
|
-
status: EmailRecoveryStatus.
|
|
10776
|
-
message: "
|
|
10901
|
+
step: 6,
|
|
10902
|
+
phase: EmailRecoveryPhase.STEP_6_COMPLETE,
|
|
10903
|
+
status: EmailRecoveryStatus.SUCCESS,
|
|
10904
|
+
message: "Email recovery completed successfully",
|
|
10777
10905
|
data: {
|
|
10778
|
-
|
|
10779
|
-
|
|
10906
|
+
accountId: rec.accountId,
|
|
10907
|
+
nearPublicKey: rec.nearPublicKey
|
|
10780
10908
|
}
|
|
10781
10909
|
});
|
|
10910
|
+
} catch (e) {
|
|
10911
|
+
const err$1 = this.emitError(5, e?.message || "Email recovery finalization failed");
|
|
10912
|
+
await this.options?.afterCall?.(false);
|
|
10913
|
+
throw err$1;
|
|
10914
|
+
}
|
|
10915
|
+
}
|
|
10916
|
+
async attemptAutoLogin(rec) {
|
|
10917
|
+
try {
|
|
10918
|
+
const accountId = toAccountId(rec.accountId);
|
|
10919
|
+
const deviceNumber = parseDeviceNumber(rec.deviceNumber, { min: 1 });
|
|
10920
|
+
if (deviceNumber === null) return this.handleAutoLoginFailure(`Invalid deviceNumber for auto-login: ${String(rec.deviceNumber)}`);
|
|
10921
|
+
const shamirUnlocked = await this.tryShamirUnlock(rec, accountId, deviceNumber);
|
|
10922
|
+
if (shamirUnlocked) return {
|
|
10923
|
+
success: true,
|
|
10924
|
+
method: "shamir"
|
|
10925
|
+
};
|
|
10926
|
+
const touchIdResult = await this.tryTouchIdUnlock(rec, accountId, deviceNumber);
|
|
10927
|
+
if (touchIdResult.success) return {
|
|
10928
|
+
success: true,
|
|
10929
|
+
method: "touchid"
|
|
10930
|
+
};
|
|
10931
|
+
return this.handleAutoLoginFailure(touchIdResult.reason || "Auto-login failed");
|
|
10932
|
+
} catch (err$1) {
|
|
10933
|
+
return this.handleAutoLoginFailure(err$1?.message || String(err$1), err$1);
|
|
10782
10934
|
}
|
|
10783
10935
|
}
|
|
10784
10936
|
};
|
|
@@ -10793,6 +10945,7 @@ init_IndexedDBManager();
|
|
|
10793
10945
|
init_actions();
|
|
10794
10946
|
init_errors();
|
|
10795
10947
|
init_rpcCalls();
|
|
10948
|
+
init_EmailRecovery();
|
|
10796
10949
|
init_defaultConfigs();
|
|
10797
10950
|
let warnedAboutSameOriginWallet = false;
|
|
10798
10951
|
/**
|
|
@@ -10849,7 +11002,7 @@ var TatchiPasskey = class {
|
|
|
10849
11002
|
} catch {}
|
|
10850
11003
|
if (!this.iframeRouter) {
|
|
10851
11004
|
if (!this.walletIframeInitInFlight) this.walletIframeInitInFlight = (async () => {
|
|
10852
|
-
const { WalletIframeRouter } = await import("./router-
|
|
11005
|
+
const { WalletIframeRouter } = await import("./router-DuGYOd3G.js");
|
|
10853
11006
|
this.iframeRouter = new WalletIframeRouter({
|
|
10854
11007
|
walletOrigin,
|
|
10855
11008
|
servicePath: walletIframeConfig?.walletServicePath || "/wallet-service",
|