decane-connect-kit 2.7.2 → 2.7.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +165 -46
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +164 -46
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -44,6 +44,7 @@ __export(errors_exports, {
|
|
|
44
44
|
NewDeviceError: () => NewDeviceError,
|
|
45
45
|
OriginNotAllowedError: () => OriginNotAllowedError,
|
|
46
46
|
PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
|
|
47
|
+
ProvisioningTimeoutError: () => ProvisioningTimeoutError,
|
|
47
48
|
ReconnectRequiredError: () => ReconnectRequiredError,
|
|
48
49
|
RecoveryFailedError: () => RecoveryFailedError,
|
|
49
50
|
RecoveryWalletMismatchError: () => RecoveryWalletMismatchError,
|
|
@@ -55,7 +56,7 @@ __export(errors_exports, {
|
|
|
55
56
|
WebAuthnNotSupportedError: () => WebAuthnNotSupportedError,
|
|
56
57
|
WrongPasswordError: () => WrongPasswordError
|
|
57
58
|
});
|
|
58
|
-
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
59
|
+
var DecaneConnectError, InvalidApiKeyError, OriginNotAllowedError, SessionExpiredError, UnlockRequiredError, ReconnectRequiredError, UserCancelledError, WebAuthnNotSupportedError, InvalidPinError, CallSequenceError, NetworkError, ShareCorruptedError, ProvisioningTimeoutError, NewDeviceError, StaleDeviceShareError, PromptPinNotConfiguredError, AttestationError, RecoveryFailedError, InvalidRecoveryFileError, WrongPasswordError, RecoveryWalletMismatchError, ExportNotAuthorizedError;
|
|
59
60
|
var init_errors = __esm({
|
|
60
61
|
"src/social/errors.ts"() {
|
|
61
62
|
"use strict";
|
|
@@ -126,6 +127,11 @@ var init_errors = __esm({
|
|
|
126
127
|
super("SHARE_CORRUPTED", "Stored device share is corrupted");
|
|
127
128
|
}
|
|
128
129
|
};
|
|
130
|
+
ProvisioningTimeoutError = class extends DecaneConnectError {
|
|
131
|
+
constructor(step) {
|
|
132
|
+
super("PROVISIONING_TIMEOUT", `Wallet setup timed out while ${step}. Check your connection and try again.`, true);
|
|
133
|
+
}
|
|
134
|
+
};
|
|
129
135
|
NewDeviceError = class extends DecaneConnectError {
|
|
130
136
|
constructor() {
|
|
131
137
|
super("NEW_DEVICE", "No device share found. Re-authenticate to set up this device.");
|
|
@@ -204,6 +210,7 @@ __export(index_exports, {
|
|
|
204
210
|
NewDeviceError: () => NewDeviceError,
|
|
205
211
|
OriginNotAllowedError: () => OriginNotAllowedError,
|
|
206
212
|
PromptPinNotConfiguredError: () => PromptPinNotConfiguredError,
|
|
213
|
+
ProvisioningTimeoutError: () => ProvisioningTimeoutError,
|
|
207
214
|
ReconnectRequiredError: () => ReconnectRequiredError,
|
|
208
215
|
RecoveryFailedError: () => RecoveryFailedError,
|
|
209
216
|
RecoveryWalletMismatchError: () => RecoveryWalletMismatchError,
|
|
@@ -2007,6 +2014,7 @@ var ERROR_MAP = {
|
|
|
2007
2014
|
// generic failure message.
|
|
2008
2015
|
UNAUTHORIZED: ReconnectRequiredError
|
|
2009
2016
|
};
|
|
2017
|
+
var REQUEST_TIMEOUT_MS = 3e4;
|
|
2010
2018
|
var ApiClient = class {
|
|
2011
2019
|
constructor(baseUrl, apiKey, appId) {
|
|
2012
2020
|
this.baseUrl = baseUrl;
|
|
@@ -2015,9 +2023,12 @@ var ApiClient = class {
|
|
|
2015
2023
|
}
|
|
2016
2024
|
async _request(path, init) {
|
|
2017
2025
|
let res;
|
|
2026
|
+
const controller = new AbortController();
|
|
2027
|
+
const timer = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
|
|
2018
2028
|
try {
|
|
2019
2029
|
res = await fetch(`${this.baseUrl}${path}`, {
|
|
2020
2030
|
...init,
|
|
2031
|
+
signal: controller.signal,
|
|
2021
2032
|
headers: {
|
|
2022
2033
|
"Content-Type": "application/json",
|
|
2023
2034
|
"X-API-Key": this.apiKey,
|
|
@@ -2027,6 +2038,8 @@ var ApiClient = class {
|
|
|
2027
2038
|
});
|
|
2028
2039
|
} catch {
|
|
2029
2040
|
throw new NetworkError();
|
|
2041
|
+
} finally {
|
|
2042
|
+
clearTimeout(timer);
|
|
2030
2043
|
}
|
|
2031
2044
|
if (res.status === 404 && init?.allow404) return null;
|
|
2032
2045
|
if (!res.ok) {
|
|
@@ -2329,6 +2342,36 @@ function bytesToBase64(b) {
|
|
|
2329
2342
|
function base64ToBytes(s) {
|
|
2330
2343
|
return Uint8Array.from(atob(s), (c) => c.charCodeAt(0));
|
|
2331
2344
|
}
|
|
2345
|
+
var TEE_TIMEOUT_MS = 3e4;
|
|
2346
|
+
async function teeFetch(url, init) {
|
|
2347
|
+
const controller = new AbortController();
|
|
2348
|
+
const timer = setTimeout(() => controller.abort(), TEE_TIMEOUT_MS);
|
|
2349
|
+
try {
|
|
2350
|
+
return await fetch(url, { ...init, signal: controller.signal });
|
|
2351
|
+
} finally {
|
|
2352
|
+
clearTimeout(timer);
|
|
2353
|
+
}
|
|
2354
|
+
}
|
|
2355
|
+
var ATTESTATION_TTL_MS = 10 * 6e4;
|
|
2356
|
+
var attestationCache = /* @__PURE__ */ new Map();
|
|
2357
|
+
function attestOnce(teeUrl, verify) {
|
|
2358
|
+
const hit = attestationCache.get(teeUrl);
|
|
2359
|
+
if (hit && Date.now() - hit.at < ATTESTATION_TTL_MS) return hit.promise;
|
|
2360
|
+
const promise = verify().catch((e) => {
|
|
2361
|
+
attestationCache.delete(teeUrl);
|
|
2362
|
+
throw e;
|
|
2363
|
+
});
|
|
2364
|
+
attestationCache.set(teeUrl, { at: Date.now(), promise });
|
|
2365
|
+
return promise;
|
|
2366
|
+
}
|
|
2367
|
+
function prewarmAttestation(teeUrl) {
|
|
2368
|
+
void attestOnce(teeUrl, async () => {
|
|
2369
|
+
const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
2370
|
+
const nonceHex = Array.from(nonce).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2371
|
+
await verifyTeeAttestation(teeUrl, nonceHex);
|
|
2372
|
+
}).catch(() => {
|
|
2373
|
+
});
|
|
2374
|
+
}
|
|
2332
2375
|
var TeeClient = class {
|
|
2333
2376
|
constructor(teeUrl) {
|
|
2334
2377
|
this.teeUrl = teeUrl;
|
|
@@ -2370,9 +2413,11 @@ var TeeClient = class {
|
|
|
2370
2413
|
}
|
|
2371
2414
|
async ensureAttested() {
|
|
2372
2415
|
if (this.attested) return;
|
|
2373
|
-
|
|
2374
|
-
|
|
2375
|
-
|
|
2416
|
+
await attestOnce(this.teeUrl, async () => {
|
|
2417
|
+
const nonce = crypto.getRandomValues(new Uint8Array(16));
|
|
2418
|
+
const nonceHex = Array.from(nonce).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
2419
|
+
await verifyTeeAttestation(this.teeUrl, nonceHex);
|
|
2420
|
+
});
|
|
2376
2421
|
this.attested = true;
|
|
2377
2422
|
}
|
|
2378
2423
|
/**
|
|
@@ -2384,7 +2429,7 @@ var TeeClient = class {
|
|
|
2384
2429
|
*/
|
|
2385
2430
|
async openSession(deviceShare, jwt, requireAssertionPerSignature = false) {
|
|
2386
2431
|
await this.ensureAttested();
|
|
2387
|
-
const res = await
|
|
2432
|
+
const res = await teeFetch(`${this.teeUrl}/signer/session`, {
|
|
2388
2433
|
method: "POST",
|
|
2389
2434
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2390
2435
|
body: JSON.stringify({
|
|
@@ -2416,7 +2461,7 @@ var TeeClient = class {
|
|
|
2416
2461
|
*/
|
|
2417
2462
|
async recoverWithShare(jwt, recoveryShare) {
|
|
2418
2463
|
await this.ensureAttested();
|
|
2419
|
-
const res = await
|
|
2464
|
+
const res = await teeFetch(`${this.teeUrl}/signer/recover`, {
|
|
2420
2465
|
method: "POST",
|
|
2421
2466
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2422
2467
|
body: JSON.stringify({ recoveryShare: bytesToBase64(recoveryShare) })
|
|
@@ -2441,7 +2486,7 @@ var TeeClient = class {
|
|
|
2441
2486
|
* require proof of fresh authenticator possession, not just an active session.
|
|
2442
2487
|
*/
|
|
2443
2488
|
async completeAssertion(jwt) {
|
|
2444
|
-
const res = await
|
|
2489
|
+
const res = await teeFetch(`${this.teeUrl}/signer/assertion-challenge`, {
|
|
2445
2490
|
method: "POST",
|
|
2446
2491
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2447
2492
|
body: JSON.stringify({ rpId: window.location.hostname })
|
|
@@ -2470,7 +2515,7 @@ var TeeClient = class {
|
|
|
2470
2515
|
*/
|
|
2471
2516
|
async enrolAuthShare(jwt, recoveryShare) {
|
|
2472
2517
|
await this.ensureAttested();
|
|
2473
|
-
const res = await
|
|
2518
|
+
const res = await teeFetch(`${this.teeUrl}/signer/auth-share`, {
|
|
2474
2519
|
method: "POST",
|
|
2475
2520
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2476
2521
|
body: JSON.stringify({ share: bytesToBase64(recoveryShare) })
|
|
@@ -2493,7 +2538,7 @@ var TeeClient = class {
|
|
|
2493
2538
|
*/
|
|
2494
2539
|
async provisionDeviceShare(jwt) {
|
|
2495
2540
|
await this.ensureAttested();
|
|
2496
|
-
const res = await
|
|
2541
|
+
const res = await teeFetch(`${this.teeUrl}/signer/provision`, {
|
|
2497
2542
|
method: "POST",
|
|
2498
2543
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" }
|
|
2499
2544
|
});
|
|
@@ -2511,7 +2556,7 @@ var TeeClient = class {
|
|
|
2511
2556
|
async rotateShares(jwt) {
|
|
2512
2557
|
if (!this.handle) throw new Error("no session");
|
|
2513
2558
|
const { challengeId, assertion } = await this.completeAssertion(jwt);
|
|
2514
|
-
const res = await
|
|
2559
|
+
const res = await teeFetch(`${this.teeUrl}/signer/rotate`, {
|
|
2515
2560
|
method: "POST",
|
|
2516
2561
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2517
2562
|
body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
|
|
@@ -2528,7 +2573,7 @@ var TeeClient = class {
|
|
|
2528
2573
|
async exportPortableBackup(jwt) {
|
|
2529
2574
|
if (!this.handle) throw new Error("no session");
|
|
2530
2575
|
const { challengeId, assertion } = await this.completeAssertion(jwt);
|
|
2531
|
-
const res = await
|
|
2576
|
+
const res = await teeFetch(`${this.teeUrl}/signer/export-portable`, {
|
|
2532
2577
|
method: "POST",
|
|
2533
2578
|
headers: { Authorization: `Bearer ${jwt}`, "Content-Type": "application/json" },
|
|
2534
2579
|
body: JSON.stringify({ handle: this.handle, challengeId, assertion, origin: window.location.origin })
|
|
@@ -2558,7 +2603,7 @@ var TeeClient = class {
|
|
|
2558
2603
|
if (!jwt) throw new Error("sign requires a jwt when per-signature assertion is enabled");
|
|
2559
2604
|
gate = await this.completeAssertion(jwt);
|
|
2560
2605
|
}
|
|
2561
|
-
const res = await
|
|
2606
|
+
const res = await teeFetch(`${this.teeUrl}/signer/sign`, {
|
|
2562
2607
|
method: "POST",
|
|
2563
2608
|
headers: { "Content-Type": "application/json" },
|
|
2564
2609
|
body: JSON.stringify({
|
|
@@ -3499,6 +3544,64 @@ async function createDecaneConnect(config) {
|
|
|
3499
3544
|
let ethProvider = null;
|
|
3500
3545
|
const persistSession = config.persistSession !== false;
|
|
3501
3546
|
const serverAssistedRecovery = true;
|
|
3547
|
+
const RECOVERY_OFFER_TIMEOUT_MS = 5 * 6e4;
|
|
3548
|
+
const selfHealAttempted = /* @__PURE__ */ new Set();
|
|
3549
|
+
async function selfHealEnrolment(userId, jwt, share) {
|
|
3550
|
+
try {
|
|
3551
|
+
if (selfHealAttempted.has(userId)) return;
|
|
3552
|
+
const status = await api.getRecoveryStatus(jwt);
|
|
3553
|
+
if (status.enrolled !== false) return;
|
|
3554
|
+
selfHealAttempted.add(userId);
|
|
3555
|
+
await new TeeClient(teeUrl).enrolAuthShare(jwt, share);
|
|
3556
|
+
} catch (e) {
|
|
3557
|
+
events.emit("enrolment-failed");
|
|
3558
|
+
console.warn(
|
|
3559
|
+
"decane-connect-kit: this wallet is not enrolled for server-assisted recovery and the automatic repair failed. It still works on this device, but a new device will need a passkey backup or a recovery file. The SDK will try again on the next unlock.",
|
|
3560
|
+
e
|
|
3561
|
+
);
|
|
3562
|
+
} finally {
|
|
3563
|
+
zeroize(share);
|
|
3564
|
+
}
|
|
3565
|
+
}
|
|
3566
|
+
async function enrolInBackground(jwt, share) {
|
|
3567
|
+
try {
|
|
3568
|
+
for (let attempt = 1; attempt <= 2; attempt++) {
|
|
3569
|
+
try {
|
|
3570
|
+
await new TeeClient(teeUrl).enrolAuthShare(jwt, share);
|
|
3571
|
+
return;
|
|
3572
|
+
} catch (e) {
|
|
3573
|
+
if (attempt === 2) {
|
|
3574
|
+
events.emit("enrolment-failed");
|
|
3575
|
+
console.warn(
|
|
3576
|
+
"decane-connect-kit: could not enrol this wallet for server-assisted recovery. The wallet is fine and fully usable; it will be re-enrolled automatically on the next unlock. Until then a brand-new device would need a passkey backup or a recovery file.",
|
|
3577
|
+
e
|
|
3578
|
+
);
|
|
3579
|
+
}
|
|
3580
|
+
}
|
|
3581
|
+
}
|
|
3582
|
+
} finally {
|
|
3583
|
+
zeroize(share);
|
|
3584
|
+
}
|
|
3585
|
+
}
|
|
3586
|
+
const PROVISIONING_BUDGET_MS = 9e4;
|
|
3587
|
+
function withDeadline(promise, ms, step) {
|
|
3588
|
+
let timer;
|
|
3589
|
+
return Promise.race([
|
|
3590
|
+
promise,
|
|
3591
|
+
new Promise((_, reject) => {
|
|
3592
|
+
timer = setTimeout(() => reject(new ProvisioningTimeoutError(step)), Math.max(0, ms));
|
|
3593
|
+
})
|
|
3594
|
+
]).finally(() => clearTimeout(timer));
|
|
3595
|
+
}
|
|
3596
|
+
function withTimeout(promise, ms) {
|
|
3597
|
+
let timer;
|
|
3598
|
+
return Promise.race([
|
|
3599
|
+
promise,
|
|
3600
|
+
new Promise((resolve) => {
|
|
3601
|
+
timer = setTimeout(() => resolve(null), ms);
|
|
3602
|
+
})
|
|
3603
|
+
]).finally(() => clearTimeout(timer));
|
|
3604
|
+
}
|
|
3502
3605
|
const SESSION_KEY = sessionPersistKey(config.apiKey);
|
|
3503
3606
|
function persistLiveSession() {
|
|
3504
3607
|
if (!persistSession) return;
|
|
@@ -3610,6 +3713,7 @@ async function createDecaneConnect(config) {
|
|
|
3610
3713
|
}
|
|
3611
3714
|
}
|
|
3612
3715
|
async function firstTimeSetup(userId, jwt) {
|
|
3716
|
+
prewarmAttestation(teeUrl);
|
|
3613
3717
|
let unlockMethod;
|
|
3614
3718
|
let wrappingKey;
|
|
3615
3719
|
let meta = {};
|
|
@@ -3639,6 +3743,8 @@ async function createDecaneConnect(config) {
|
|
|
3639
3743
|
({ wrappingKey, meta } = await pinSetup());
|
|
3640
3744
|
unlockMethod = "pin";
|
|
3641
3745
|
}
|
|
3746
|
+
const provisioningStartedAt = Date.now();
|
|
3747
|
+
const remainingBudget = () => PROVISIONING_BUDGET_MS - (Date.now() - provisioningStartedAt);
|
|
3642
3748
|
const master = generateMaster();
|
|
3643
3749
|
let shareSet;
|
|
3644
3750
|
let evmAddress;
|
|
@@ -3670,7 +3776,7 @@ async function createDecaneConnect(config) {
|
|
|
3670
3776
|
version: 1
|
|
3671
3777
|
});
|
|
3672
3778
|
const serverIv = generateRandomBytes(12);
|
|
3673
|
-
await api.putShare(jwt, {
|
|
3779
|
+
await withDeadline(api.putShare(jwt, {
|
|
3674
3780
|
ciphertext: shareSet.server,
|
|
3675
3781
|
iv: serverIv,
|
|
3676
3782
|
wrappedShareMeta: {
|
|
@@ -3680,7 +3786,7 @@ async function createDecaneConnect(config) {
|
|
|
3680
3786
|
evmAddress,
|
|
3681
3787
|
solanaAddress,
|
|
3682
3788
|
tronAddress
|
|
3683
|
-
});
|
|
3789
|
+
}), remainingBudget(), "storing your wallet");
|
|
3684
3790
|
zeroize(shareSet.server);
|
|
3685
3791
|
const addresses = { evm: evmAddress, solana: solanaAddress, tron: tronAddress };
|
|
3686
3792
|
if (unlockMethod === "passkey" && meta.credentialId && meta.prfSalt) {
|
|
@@ -3696,7 +3802,11 @@ async function createDecaneConnect(config) {
|
|
|
3696
3802
|
console.warn("decane-connect-kit: wrapped share backup failed; user can retry from settings.", e);
|
|
3697
3803
|
}
|
|
3698
3804
|
}
|
|
3699
|
-
const confirmed = await
|
|
3805
|
+
const confirmed = await withDeadline(
|
|
3806
|
+
session.start(shareSet.device, jwt, addresses),
|
|
3807
|
+
remainingBudget(),
|
|
3808
|
+
"opening the signing session"
|
|
3809
|
+
);
|
|
3700
3810
|
zeroize(shareSet.device);
|
|
3701
3811
|
if (confirmed.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
|
|
3702
3812
|
console.warn(
|
|
@@ -3707,42 +3817,49 @@ async function createDecaneConnect(config) {
|
|
|
3707
3817
|
addresses.solana = solanaAddress = confirmed.solana;
|
|
3708
3818
|
addresses.tron = tronAddress = confirmed.tron ?? evmToTronAddress2(confirmed.evm);
|
|
3709
3819
|
if (serverAssistedRecovery) {
|
|
3710
|
-
|
|
3711
|
-
try {
|
|
3712
|
-
const tee = new TeeClient(teeUrl);
|
|
3713
|
-
await tee.enrolAuthShare(jwt, shareSet.recovery);
|
|
3714
|
-
break;
|
|
3715
|
-
} catch (e) {
|
|
3716
|
-
if (attempt === 2) {
|
|
3717
|
-
console.warn(
|
|
3718
|
-
"decane-connect-kit: could not enrol this wallet for server-assisted recovery; a recovery file will be required on a new device. This wallet will get a 409 not_enrolled from /signer/provision until it is re-enrolled.",
|
|
3719
|
-
e
|
|
3720
|
-
);
|
|
3721
|
-
}
|
|
3722
|
-
}
|
|
3723
|
-
}
|
|
3820
|
+
void enrolInBackground(jwt, new Uint8Array(shareSet.recovery));
|
|
3724
3821
|
}
|
|
3725
|
-
if (config.offerRecoveryAtSignup
|
|
3726
|
-
|
|
3727
|
-
|
|
3728
|
-
|
|
3729
|
-
|
|
3730
|
-
|
|
3731
|
-
|
|
3732
|
-
|
|
3733
|
-
|
|
3734
|
-
choice.passwordHint
|
|
3735
|
-
);
|
|
3736
|
-
await config.onRecoveryFileReady?.(file, recoveryShareFileName(evmAddress));
|
|
3737
|
-
await api.markRecoveryShareSaved(jwt);
|
|
3738
|
-
}
|
|
3739
|
-
} catch (e) {
|
|
3740
|
-
console.warn("decane-connect-kit: recovery share offer failed or was skipped.", e);
|
|
3741
|
-
}
|
|
3822
|
+
if (config.offerRecoveryAtSignup === true && config.onRecoveryShareOffer) {
|
|
3823
|
+
void offerRecoveryShare(
|
|
3824
|
+
jwt,
|
|
3825
|
+
new Uint8Array(shareSet.recovery),
|
|
3826
|
+
{ ...addresses },
|
|
3827
|
+
shareSet.version,
|
|
3828
|
+
evmAddress,
|
|
3829
|
+
solanaAddress
|
|
3830
|
+
);
|
|
3742
3831
|
}
|
|
3743
3832
|
zeroize(shareSet.recovery);
|
|
3744
3833
|
return addresses;
|
|
3745
3834
|
}
|
|
3835
|
+
async function offerRecoveryShare(jwt, recoveryShare, addresses, shareSetVersion, evmAddress, solanaAddress) {
|
|
3836
|
+
try {
|
|
3837
|
+
const choice = await withTimeout(
|
|
3838
|
+
config.onRecoveryShareOffer({ evmAddress, solanaAddress }),
|
|
3839
|
+
RECOVERY_OFFER_TIMEOUT_MS
|
|
3840
|
+
);
|
|
3841
|
+
if (!choice) {
|
|
3842
|
+
console.warn(
|
|
3843
|
+
`decane-connect-kit: onRecoveryShareOffer did not settle within ${RECOVERY_OFFER_TIMEOUT_MS / 6e4} minutes, so no recovery file was offered. The wallet is unaffected \u2014 it was already created and enrolled \u2014 and the user can generate a recovery share later via rotateShares(). Check that your handler always resolves or rejects.`
|
|
3844
|
+
);
|
|
3845
|
+
return;
|
|
3846
|
+
}
|
|
3847
|
+
if (!choice.wants) return;
|
|
3848
|
+
const file = await buildRecoveryShareFile(
|
|
3849
|
+
recoveryShare,
|
|
3850
|
+
choice.password,
|
|
3851
|
+
addresses,
|
|
3852
|
+
shareSetVersion,
|
|
3853
|
+
choice.passwordHint
|
|
3854
|
+
);
|
|
3855
|
+
await config.onRecoveryFileReady?.(file, recoveryShareFileName(evmAddress));
|
|
3856
|
+
await api.markRecoveryShareSaved(jwt);
|
|
3857
|
+
} catch (e) {
|
|
3858
|
+
console.warn("decane-connect-kit: recovery share offer failed or was skipped.", e);
|
|
3859
|
+
} finally {
|
|
3860
|
+
zeroize(recoveryShare);
|
|
3861
|
+
}
|
|
3862
|
+
}
|
|
3746
3863
|
async function returningUserUnlock(userId, jwt) {
|
|
3747
3864
|
const rawAddresses = await api.getAddresses(jwt);
|
|
3748
3865
|
const recordedAddresses = rawAddresses.evm && rawAddresses.solana ? { evm: rawAddresses.evm, solana: rawAddresses.solana, tron: rawAddresses.tron ?? evmToTronAddress2(rawAddresses.evm) } : null;
|
|
@@ -3828,6 +3945,7 @@ async function createDecaneConnect(config) {
|
|
|
3828
3945
|
}
|
|
3829
3946
|
throw e;
|
|
3830
3947
|
}
|
|
3948
|
+
void selfHealEnrolment(userId, jwt, new Uint8Array(deviceShare));
|
|
3831
3949
|
zeroize(deviceShare);
|
|
3832
3950
|
if (recordedAddresses && recordedAddresses.evm.toLowerCase() !== addresses.evm.toLowerCase()) {
|
|
3833
3951
|
console.warn(
|
|
@@ -7273,6 +7391,7 @@ function useSignTypedData() {
|
|
|
7273
7391
|
NewDeviceError,
|
|
7274
7392
|
OriginNotAllowedError,
|
|
7275
7393
|
PromptPinNotConfiguredError,
|
|
7394
|
+
ProvisioningTimeoutError,
|
|
7276
7395
|
ReconnectRequiredError,
|
|
7277
7396
|
RecoveryFailedError,
|
|
7278
7397
|
RecoveryWalletMismatchError,
|