@accesly/react 1.0.0-pre.1 → 1.0.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/CHANGELOG.md +39 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/index.cjs +145 -3
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +112 -7
- package/dist/index.d.ts +112 -7
- package/dist/index.js +146 -4
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord } from '@accesly/core';
|
|
1
|
+
import { Environment, CognitoConfig, AuthClient, SessionStorage, DeviceStore, TelemetrySink, TokenManager, AccesslyEndpoints, AuthStatus, CredentialRecord, EncryptedEnvelope } from '@accesly/core';
|
|
2
2
|
import * as react from 'react';
|
|
3
3
|
import { ReactNode } from 'react';
|
|
4
4
|
|
|
@@ -444,6 +444,88 @@ declare class NotImplementedYetError extends Error {
|
|
|
444
444
|
* - Persiste new CredentialRecord local
|
|
445
445
|
* - Zero-iza la seed
|
|
446
446
|
*/
|
|
447
|
+
interface ReconstructedSeed {
|
|
448
|
+
/** 32-byte ed25519 seed reconstruida vía Shamir(F2_recovery + F3). CALLER ZEROIZE. */
|
|
449
|
+
readonly privateSeed: Uint8Array;
|
|
450
|
+
/** 32-byte ed25519 public key derivada. */
|
|
451
|
+
readonly publicKey: Uint8Array;
|
|
452
|
+
/** 32-byte recoveryKey derivada del password — útil para re-cifrar F2'/F3' nuevos. */
|
|
453
|
+
readonly recoveryKey: Uint8Array;
|
|
454
|
+
/** Base64 32-byte salt que vino del backend. */
|
|
455
|
+
readonly recoverySalt: string;
|
|
456
|
+
}
|
|
457
|
+
/**
|
|
458
|
+
* Input para `recovery.finalize(...)` — orquestador end-to-end de la rotación
|
|
459
|
+
* de signers tras una recuperación por OTP.
|
|
460
|
+
*
|
|
461
|
+
* El caller debe pre-cumplir 2 pasos UI:
|
|
462
|
+
* a) `reconstructSeed(...)` — devolvió `privateSeed` (la VIEJA, va a firmar
|
|
463
|
+
* la auth entry) + `publicKey` (la VIEJA, no se rota acá).
|
|
464
|
+
* b) WebAuthn `navigator.credentials.create()` con PRF extension — devolvió
|
|
465
|
+
* `credentialId`, `secp256r1Pubkey`, y un output PRF que el caller
|
|
466
|
+
* derivó en 2 keys `newF1Key` + `newF2Key` (HKDF típicamente).
|
|
467
|
+
*
|
|
468
|
+
* El SDK hace todo lo demás client-side:
|
|
469
|
+
* 1. Genera fresh ed25519 seed (la NUEVA) + Shamir 2-of-3 split.
|
|
470
|
+
* 2. Cifra F1' con `newF1Key`, F2' con `newF2Key`, F3' con `recoveryKey`.
|
|
471
|
+
* 3. Cifra una segunda copia de F2' con `recoveryKey` (F2_recovery) para
|
|
472
|
+
* que la siguiente recovery siga siendo posible.
|
|
473
|
+
* 4. POST /recovery/simulate-rotate-signer → backend simula + devuelve material.
|
|
474
|
+
* 5. Firma la auth entry con la SEED VIEJA contra la regla `admin-cfg`.
|
|
475
|
+
* 6. POST /recovery/finalize con auth entry firmada + new fragments.
|
|
476
|
+
* 7. Persiste el nuevo `CredentialRecord` en `DeviceStore` (sustituye el viejo).
|
|
477
|
+
* 8. Zeroiza todas las llaves intermedias (la seed vieja la entrega el caller,
|
|
478
|
+
* su lifecycle es responsabilidad del caller).
|
|
479
|
+
*/
|
|
480
|
+
interface FinalizeRecoveryInput {
|
|
481
|
+
/** Email del usuario (case-insensitive, se normaliza). */
|
|
482
|
+
readonly email: string;
|
|
483
|
+
/**
|
|
484
|
+
* Password de Cognito (UTF-8 bytes). El SDK la usa SOLO para derivar el
|
|
485
|
+
* `newRecoveryKey` con `PBKDF2(password, newRecoverySalt, 600k)`. Caller
|
|
486
|
+
* debe zeroizar tras la llamada.
|
|
487
|
+
*/
|
|
488
|
+
readonly cognitoPassword: Uint8Array;
|
|
489
|
+
/** Token KMS-HMAC que devolvió `verifyOtp()` — TTL 5min. */
|
|
490
|
+
readonly recoveryJwt: string;
|
|
491
|
+
/**
|
|
492
|
+
* La seed VIEJA reconstruida por `reconstructSeed()`. Se usa SOLO para
|
|
493
|
+
* firmar la `SorobanAuthorizationEntry` de `rotate_signer` (la regla
|
|
494
|
+
* `admin-cfg` valida con el ed25519 pubkey actual del Smart Account, que
|
|
495
|
+
* es el del owner viejo). Tras firmar, el SDK la zeroiza internamente.
|
|
496
|
+
*
|
|
497
|
+
* IMPORTANTE: pasa `seedResult.privateSeed` tal cual te lo dio
|
|
498
|
+
* `reconstructSeed()`. Si ya lo zeroizaste, falla.
|
|
499
|
+
*/
|
|
500
|
+
readonly oldReconstructedSeed: Uint8Array;
|
|
501
|
+
/**
|
|
502
|
+
* La pubkey VIEJA (32 bytes) — derivable de `oldReconstructedSeed`, pero
|
|
503
|
+
* la pedimos explícita para sanity-check y para empaquetar dentro del
|
|
504
|
+
* `Signer::External(verifier, pubkey)` del AuthPayload.
|
|
505
|
+
*/
|
|
506
|
+
readonly oldOwnerPubkey: Uint8Array;
|
|
507
|
+
/** Credential ID del nuevo passkey (de `navigator.credentials.create`). */
|
|
508
|
+
readonly newCredentialId: Uint8Array;
|
|
509
|
+
/** Pubkey secp256r1 uncompressed (65 bytes) del nuevo passkey. */
|
|
510
|
+
readonly newSecp256r1Pubkey: Uint8Array;
|
|
511
|
+
/** Salt PRF del nuevo passkey (32 bytes random). */
|
|
512
|
+
readonly newPrfSalt: Uint8Array;
|
|
513
|
+
/** AES-256 key derivada del PRF output para cifrar F1'. */
|
|
514
|
+
readonly newF1Key: Uint8Array;
|
|
515
|
+
/** AES-256 key derivada del PRF output para cifrar F2' (PRF-bound, sign path). */
|
|
516
|
+
readonly newF2Key: Uint8Array;
|
|
517
|
+
/** Salt aleatorio (32 bytes) para el nuevo emailCommitment. */
|
|
518
|
+
readonly newEmailSalt: Uint8Array;
|
|
519
|
+
}
|
|
520
|
+
interface FinalizeRecoveryResult {
|
|
521
|
+
readonly walletAddress: string;
|
|
522
|
+
readonly txHash: string;
|
|
523
|
+
readonly status: string;
|
|
524
|
+
/** Pubkey ed25519 NUEVA (32 bytes). Útil para UI confirmación. */
|
|
525
|
+
readonly newPublicKey: Uint8Array;
|
|
526
|
+
/** Link al explorer. */
|
|
527
|
+
readonly explorerUrl: string;
|
|
528
|
+
}
|
|
447
529
|
interface RecoveryNamespace {
|
|
448
530
|
/** Pide OTP. Backend rate-limita; el caller debe respetar `cooldownSeconds`. */
|
|
449
531
|
requestOtp(input: {
|
|
@@ -461,19 +543,42 @@ interface RecoveryNamespace {
|
|
|
461
543
|
expiresAt: number;
|
|
462
544
|
}>;
|
|
463
545
|
/**
|
|
464
|
-
*
|
|
465
|
-
*
|
|
466
|
-
*
|
|
546
|
+
* Descarga `/fragments/3`, descifra F2_recovery + F3 con la `recoveryKey`
|
|
547
|
+
* derivada del password y reconstruye la seed via Shamir.
|
|
548
|
+
*
|
|
549
|
+
* El caller DEBE zero-izar `result.privateSeed` y `result.recoveryKey`
|
|
550
|
+
* tras firmar la rotación + cifrar las nuevas F1'/F2'/F3'.
|
|
467
551
|
*
|
|
468
|
-
* El caller es responsable de zeroizar `cognitoPassword` después.
|
|
552
|
+
* El caller también es responsable de zeroizar `cognitoPassword` después.
|
|
469
553
|
*/
|
|
470
|
-
|
|
471
|
-
email: string;
|
|
554
|
+
reconstructSeed(input: {
|
|
472
555
|
cognitoPassword: Uint8Array;
|
|
473
556
|
recoveryJwt: string;
|
|
557
|
+
}): Promise<ReconstructedSeed>;
|
|
558
|
+
/**
|
|
559
|
+
* Orquestador completo de la rotación de signers para Recovery v2.
|
|
560
|
+
* Ver `FinalizeRecoveryInput` para los pre-requisitos.
|
|
561
|
+
*/
|
|
562
|
+
finalize(input: FinalizeRecoveryInput): Promise<FinalizeRecoveryResult>;
|
|
563
|
+
/**
|
|
564
|
+
* Bajo nivel: submitea la rotación al backend tras que el caller haya
|
|
565
|
+
* armado el body manualmente. `finalize(...)` es el wrapper recomendado.
|
|
566
|
+
*/
|
|
567
|
+
submitFinalize(input: {
|
|
568
|
+
recoveryJwt: string;
|
|
569
|
+
unsignedXdr: string;
|
|
570
|
+
signedAuthEntryXdr: string;
|
|
571
|
+
newSecp256r1Pubkey: string;
|
|
572
|
+
newFragmentF1Encrypted: EncryptedEnvelope;
|
|
573
|
+
newFragmentF2Encrypted: EncryptedEnvelope;
|
|
574
|
+
newFragmentF2Recovery: EncryptedEnvelope;
|
|
575
|
+
newFragmentF3Encrypted: EncryptedEnvelope;
|
|
576
|
+
newRecoverySalt: string;
|
|
577
|
+
newEmailCommitment: string;
|
|
474
578
|
}): Promise<{
|
|
475
579
|
walletAddress: string;
|
|
476
580
|
txHash: string;
|
|
581
|
+
status: string;
|
|
477
582
|
}>;
|
|
478
583
|
}
|
|
479
584
|
interface AcceslyHook {
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CognitoAuthClient, InMemorySessionStorage, InMemoryDeviceStore, TokenManager, AccesslyApiClient, AccesslyEndpoints, normalizeSecp256r1Pubkey, createWallet, generateRecoverySalt, decryptAesGcm, deriveRecoveryKey, encryptAesGcm, emailHashBytes, computeSmartAccountAddress, signTransaction, generateX25519Keypair, unwrapSessionFragment2, reconstructFromPlainAndEncrypted, signSorobanAuthEntry, AccesslyApiError } from '@accesly/core';
|
|
1
|
+
import { CognitoAuthClient, InMemorySessionStorage, InMemoryDeviceStore, TokenManager, AccesslyApiClient, AccesslyEndpoints, normalizeSecp256r1Pubkey, createWallet, generateRecoverySalt, decryptAesGcm, deriveRecoveryKey, encryptAesGcm, emailHashBytes, computeSmartAccountAddress, signTransaction, generateX25519Keypair, unwrapSessionFragment2, reconstructFromPlainAndEncrypted, signSorobanAuthEntry, reconstructKey, AccesslyApiError } from '@accesly/core';
|
|
2
2
|
import { createContext, useMemo, useState, useRef, useEffect, useContext } from 'react';
|
|
3
3
|
import { jsx } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -190,6 +190,7 @@ function useAccesly() {
|
|
|
190
190
|
secp256r1Pubkey: hexFromBytes(params.secp256r1Pubkey),
|
|
191
191
|
fragmentF2: encodeFragmentToWire(params.fragmentF2),
|
|
192
192
|
fragmentF3: encodeFragmentToWire(params.fragmentF3),
|
|
193
|
+
...params.fragmentF2Recovery ? { fragmentF2Recovery: encodeFragmentToWire(params.fragmentF2Recovery) } : {},
|
|
193
194
|
...params.emailHash ? { emailHash: params.emailHash } : {},
|
|
194
195
|
...params.recoverySalt ? { recoverySalt: params.recoverySalt } : {}
|
|
195
196
|
});
|
|
@@ -273,18 +274,22 @@ function useAccesly() {
|
|
|
273
274
|
encryptionKeys: input.encryptionKeys
|
|
274
275
|
});
|
|
275
276
|
let fragmentF3ToSend = created.encryptedFragments[2];
|
|
277
|
+
let fragmentF2Recovery;
|
|
276
278
|
let recoverySaltBase64;
|
|
277
279
|
if (input.cognitoPassword) {
|
|
278
280
|
const recoverySalt = generateRecoverySalt();
|
|
281
|
+
const f2Plain = decryptAesGcm(created.encryptedFragments[1], input.encryptionKeys[1]);
|
|
279
282
|
const f3Plain = decryptAesGcm(created.encryptedFragments[2], input.encryptionKeys[2]);
|
|
280
283
|
const recoveryKey = deriveRecoveryKey({
|
|
281
284
|
password: input.cognitoPassword,
|
|
282
285
|
salt: recoverySalt
|
|
283
286
|
});
|
|
284
287
|
try {
|
|
288
|
+
fragmentF2Recovery = encryptAesGcm(f2Plain, recoveryKey);
|
|
285
289
|
fragmentF3ToSend = encryptAesGcm(f3Plain, recoveryKey);
|
|
286
290
|
} finally {
|
|
287
291
|
for (let i = 0; i < recoveryKey.length; i += 1) recoveryKey[i] = 0;
|
|
292
|
+
for (let i = 0; i < f2Plain.length; i += 1) f2Plain[i] = 0;
|
|
288
293
|
for (let i = 0; i < f3Plain.length; i += 1) f3Plain[i] = 0;
|
|
289
294
|
}
|
|
290
295
|
recoverySaltBase64 = base64FromBytes(recoverySalt);
|
|
@@ -324,6 +329,7 @@ function useAccesly() {
|
|
|
324
329
|
fragmentF2: created.encryptedFragments[1],
|
|
325
330
|
fragmentF3: fragmentF3ToSend,
|
|
326
331
|
emailHash: emailHashHex,
|
|
332
|
+
...fragmentF2Recovery ? { fragmentF2Recovery } : {},
|
|
327
333
|
...recoverySaltBase64 ? { recoverySalt: recoverySaltBase64 } : {}
|
|
328
334
|
});
|
|
329
335
|
} catch (err) {
|
|
@@ -533,11 +539,147 @@ function useAccesly() {
|
|
|
533
539
|
async verifyOtp(input) {
|
|
534
540
|
return ctx.endpoints.verifyRecoveryOtp(input);
|
|
535
541
|
},
|
|
536
|
-
async
|
|
537
|
-
|
|
542
|
+
async reconstructSeed(input) {
|
|
543
|
+
const frag = await ctx.endpoints.getFragment3(input.recoveryJwt);
|
|
544
|
+
if (!frag.fragmentF2Recovery) {
|
|
545
|
+
throw new Error(
|
|
546
|
+
"recovery.reconstructSeed: la wallet fue creada antes de Fase 1 y no tiene F2 cipher-bound a recoveryKey. No es recuperable v\xEDa OTP."
|
|
547
|
+
);
|
|
548
|
+
}
|
|
549
|
+
const recoverySalt = base64ToBytes(frag.recoverySalt);
|
|
550
|
+
const recoveryKey = deriveRecoveryKey({
|
|
551
|
+
password: input.cognitoPassword,
|
|
552
|
+
salt: recoverySalt
|
|
553
|
+
});
|
|
554
|
+
const f2Envelope = {
|
|
555
|
+
ciphertext: base64ToBytes(frag.fragmentF2Recovery.ciphertext),
|
|
556
|
+
nonce: base64ToBytes(frag.fragmentF2Recovery.nonce)
|
|
557
|
+
};
|
|
558
|
+
const f3Envelope = {
|
|
559
|
+
ciphertext: base64ToBytes(frag.fragmentF3Encrypted.ciphertext),
|
|
560
|
+
nonce: base64ToBytes(frag.fragmentF3Encrypted.nonce)
|
|
561
|
+
};
|
|
562
|
+
const seedResult = reconstructKey({
|
|
563
|
+
fragments: [
|
|
564
|
+
{ envelope: f2Envelope, key: recoveryKey },
|
|
565
|
+
{ envelope: f3Envelope, key: recoveryKey }
|
|
566
|
+
]
|
|
567
|
+
});
|
|
568
|
+
return {
|
|
569
|
+
privateSeed: seedResult.privateSeed,
|
|
570
|
+
publicKey: seedResult.publicKey,
|
|
571
|
+
recoveryKey,
|
|
572
|
+
recoverySalt: frag.recoverySalt
|
|
573
|
+
};
|
|
574
|
+
},
|
|
575
|
+
async finalize(input) {
|
|
576
|
+
const networkPassphrase = stellarConfig.networkPassphrase;
|
|
577
|
+
const verifierAddress = stellarConfig.ed25519VerifierAddress;
|
|
578
|
+
const explorerBase = networkPassphrase === "Public Global Stellar Network ; September 2015" ? "https://stellar.expert/explorer/public/tx/" : "https://stellar.expert/explorer/testnet/tx/";
|
|
579
|
+
if (input.oldReconstructedSeed.length !== 32) {
|
|
580
|
+
throw new Error(
|
|
581
|
+
`recovery.finalize: oldReconstructedSeed must be 32 bytes, got ${input.oldReconstructedSeed.length}`
|
|
582
|
+
);
|
|
583
|
+
}
|
|
584
|
+
if (input.oldOwnerPubkey.length !== 32) {
|
|
585
|
+
throw new Error(
|
|
586
|
+
`recovery.finalize: oldOwnerPubkey must be 32 bytes, got ${input.oldOwnerPubkey.length}`
|
|
587
|
+
);
|
|
588
|
+
}
|
|
589
|
+
if (input.newSecp256r1Pubkey.length !== 65) {
|
|
590
|
+
throw new Error(
|
|
591
|
+
`recovery.finalize: newSecp256r1Pubkey must be 65 bytes (uncompressed), got ${input.newSecp256r1Pubkey.length}`
|
|
592
|
+
);
|
|
593
|
+
}
|
|
594
|
+
const newRecoverySalt = generateRecoverySalt();
|
|
595
|
+
const newRecoveryKey = deriveRecoveryKey({
|
|
596
|
+
password: input.cognitoPassword,
|
|
597
|
+
salt: newRecoverySalt
|
|
598
|
+
});
|
|
599
|
+
const newRecoverySaltBase64 = base64FromBytes(newRecoverySalt);
|
|
600
|
+
const created = createWallet({
|
|
601
|
+
emailBytes: new TextEncoder().encode(input.email),
|
|
602
|
+
emailSalt: input.newEmailSalt,
|
|
603
|
+
encryptionKeys: [input.newF1Key, input.newF2Key, newRecoveryKey]
|
|
604
|
+
});
|
|
605
|
+
const f2PlainShare = decryptAesGcm(created.encryptedFragments[1], input.newF2Key);
|
|
606
|
+
let newFragmentF2Recovery;
|
|
607
|
+
try {
|
|
608
|
+
newFragmentF2Recovery = encryptAesGcm(f2PlainShare, newRecoveryKey);
|
|
609
|
+
} finally {
|
|
610
|
+
for (let i = 0; i < f2PlainShare.length; i += 1) f2PlainShare[i] = 0;
|
|
611
|
+
}
|
|
612
|
+
const newSecp256r1Canonical = normalizeSecp256r1Pubkey(input.newSecp256r1Pubkey);
|
|
613
|
+
const newOwnerHex = hexFromBytes(created.publicKey);
|
|
614
|
+
const newSecpHex = hexFromBytes(newSecp256r1Canonical);
|
|
615
|
+
const newEmailCommitHex = hexFromBytes(created.emailCommitment);
|
|
616
|
+
const sim = await ctx.endpoints.simulateRotateSigner(input.recoveryJwt, {
|
|
617
|
+
newOwnerEd25519Pubkey: newOwnerHex,
|
|
618
|
+
newSecp256r1Pubkey: newSecpHex,
|
|
619
|
+
newEmailCommitment: newEmailCommitHex
|
|
620
|
+
});
|
|
621
|
+
const { signedAuthEntryXdr } = await signSorobanAuthEntry({
|
|
622
|
+
signaturePayloadHashBase64: sim.signaturePayloadHashBase64,
|
|
623
|
+
contextRuleIds: [...sim.contextRuleIds],
|
|
624
|
+
placeholderAuthEntryXdr: sim.placeholderAuthEntryXdr,
|
|
625
|
+
ed25519Seed: input.oldReconstructedSeed,
|
|
626
|
+
ed25519VerifierAddress: verifierAddress,
|
|
627
|
+
ownerPubkey: input.oldOwnerPubkey
|
|
628
|
+
});
|
|
629
|
+
let finalize;
|
|
630
|
+
try {
|
|
631
|
+
finalize = await ctx.endpoints.finalizeRecovery(input.recoveryJwt, {
|
|
632
|
+
unsignedXdr: sim.unsignedXdr,
|
|
633
|
+
signedAuthEntryXdr,
|
|
634
|
+
newSecp256r1Pubkey: newSecpHex,
|
|
635
|
+
newFragmentF1Encrypted: encodeFragmentToWire(created.encryptedFragments[0]),
|
|
636
|
+
newFragmentF2Encrypted: encodeFragmentToWire(created.encryptedFragments[1]),
|
|
637
|
+
newFragmentF2Recovery: encodeFragmentToWire(newFragmentF2Recovery),
|
|
638
|
+
newFragmentF3Encrypted: encodeFragmentToWire(created.encryptedFragments[2]),
|
|
639
|
+
newRecoverySalt: newRecoverySaltBase64,
|
|
640
|
+
newEmailCommitment: newEmailCommitHex
|
|
641
|
+
});
|
|
642
|
+
} finally {
|
|
643
|
+
for (let i = 0; i < newRecoveryKey.length; i += 1) newRecoveryKey[i] = 0;
|
|
644
|
+
}
|
|
645
|
+
await ctx.deviceStore.saveCredential({
|
|
646
|
+
username: input.email,
|
|
647
|
+
credentialId: input.newCredentialId,
|
|
648
|
+
secp256r1Pubkey: newSecp256r1Canonical,
|
|
649
|
+
fragmentF1Encrypted: created.encryptedFragments[0],
|
|
650
|
+
fragmentF2Encrypted: created.encryptedFragments[1],
|
|
651
|
+
fragmentF3Encrypted: created.encryptedFragments[2],
|
|
652
|
+
publicKey: created.publicKey,
|
|
653
|
+
emailCommitment: created.emailCommitment,
|
|
654
|
+
prfSalt: input.newPrfSalt,
|
|
655
|
+
fallbackKeyMaterial: new Uint8Array(0),
|
|
656
|
+
walletAddress: finalize.walletAddress,
|
|
657
|
+
onChain: true,
|
|
658
|
+
createdAt: Date.now()
|
|
659
|
+
});
|
|
660
|
+
return {
|
|
661
|
+
walletAddress: finalize.walletAddress,
|
|
662
|
+
txHash: finalize.txHash,
|
|
663
|
+
status: finalize.status,
|
|
664
|
+
newPublicKey: created.publicKey,
|
|
665
|
+
explorerUrl: `${explorerBase}${finalize.txHash}`
|
|
666
|
+
};
|
|
667
|
+
},
|
|
668
|
+
async submitFinalize(input) {
|
|
669
|
+
return ctx.endpoints.finalizeRecovery(input.recoveryJwt, {
|
|
670
|
+
unsignedXdr: input.unsignedXdr,
|
|
671
|
+
signedAuthEntryXdr: input.signedAuthEntryXdr,
|
|
672
|
+
newSecp256r1Pubkey: input.newSecp256r1Pubkey,
|
|
673
|
+
newFragmentF1Encrypted: encodeFragmentToWire(input.newFragmentF1Encrypted),
|
|
674
|
+
newFragmentF2Encrypted: encodeFragmentToWire(input.newFragmentF2Encrypted),
|
|
675
|
+
newFragmentF2Recovery: encodeFragmentToWire(input.newFragmentF2Recovery),
|
|
676
|
+
newFragmentF3Encrypted: encodeFragmentToWire(input.newFragmentF3Encrypted),
|
|
677
|
+
newRecoverySalt: input.newRecoverySalt,
|
|
678
|
+
newEmailCommitment: input.newEmailCommitment
|
|
679
|
+
});
|
|
538
680
|
}
|
|
539
681
|
}),
|
|
540
|
-
[ctx]
|
|
682
|
+
[ctx, stellarConfig, hexFromBytes]
|
|
541
683
|
);
|
|
542
684
|
const session = useMemo(
|
|
543
685
|
() => ({
|