From 7a24fc217ae056282189edba2157e766da741a9a Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Tue, 17 Dec 2024 20:08:15 +1100 Subject: [PATCH 1/7] Moved passkey to background services --- .gitignore | 2 + .vscode/settings.json | 3 +- .../controller/provider/controller.ts | 4 +- src/background/controller/wallet.ts | 13 +-- src/background/service/flowns.ts | 2 +- src/background/service/userWallet.ts | 6 +- .../utils/modules/CborSimpleDecoder.ts} | 14 ++- .../utils/modules/Crypto.ts} | 14 ++- .../utils/modules/Signature.ts} | 14 ++- .../utils/modules/WebAuthnDecoder.ts} | 65 ++++++++---- .../utils/modules/WebAuthnTypes.ts} | 50 +++++----- .../utils/modules/base64.ts} | 23 ++--- .../utils/modules/findAddressWithPK.ts} | 0 .../utils/modules/findAddressWithPubKey.ts} | 0 .../utils/modules/passkey.ts} | 58 +++++++---- src/background/utils/modules/settings.ts | 91 +++++++++++++++++ src/background/utils/modules/utils.ts | 15 +++ src/background/webapi/storage.ts | 29 +++--- src/shared/types/algo-types.ts | 3 + src/shared/types/tracking-types.ts | 11 +-- .../utils/algo-constants.ts} | 6 +- src/ui/utils/modules/common.js | 20 ---- src/ui/utils/modules/constants.ts | 1 + src/ui/utils/modules/findAddressWithPK.ts | 4 + src/ui/utils/modules/passkey.ts | 1 + src/ui/utils/modules/settings.js | 99 ------------------- src/ui/utils/modules/utils.js | 27 ----- tsconfig.json | 2 +- 28 files changed, 284 insertions(+), 293 deletions(-) rename src/{ui/utils/modules/CborSimpleDecoder.js => background/utils/modules/CborSimpleDecoder.ts} (90%) rename src/{ui/utils/modules/Crypto.js => background/utils/modules/Crypto.ts} (93%) rename src/{ui/utils/modules/Signature.js => background/utils/modules/Signature.ts} (83%) rename src/{ui/utils/modules/WebAuthnDecoder.js => background/utils/modules/WebAuthnDecoder.ts} (63%) rename src/{ui/utils/modules/WebAuthnTypes.js => background/utils/modules/WebAuthnTypes.ts} (92%) rename src/{ui/utils/modules/base64.js => background/utils/modules/base64.ts} (74%) rename src/{ui/utils/modules/findAddressWithPK.tsx => background/utils/modules/findAddressWithPK.ts} (100%) rename src/{ui/utils/modules/findAddressWithPubKey.tsx => background/utils/modules/findAddressWithPubKey.ts} (100%) rename src/{ui/utils/modules/passkey.js => background/utils/modules/passkey.ts} (85%) create mode 100644 src/background/utils/modules/settings.ts create mode 100644 src/background/utils/modules/utils.ts rename src/{ui/utils/modules/constants.tsx => shared/utils/algo-constants.ts} (66%) delete mode 100644 src/ui/utils/modules/common.js create mode 100644 src/ui/utils/modules/constants.ts create mode 100644 src/ui/utils/modules/findAddressWithPK.ts create mode 100644 src/ui/utils/modules/passkey.ts delete mode 100644 src/ui/utils/modules/settings.js delete mode 100644 src/ui/utils/modules/utils.js diff --git a/.gitignore b/.gitignore index 6e1aa0b3..03cef6b4 100644 --- a/.gitignore +++ b/.gitignore @@ -23,3 +23,5 @@ dist.7z /playwright-report/ /blob-report/ /playwright/.cache/ +dependency-usage.md +extension-dependencies.md diff --git a/.vscode/settings.json b/.vscode/settings.json index cd384a8c..91342cd5 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -21,5 +21,6 @@ "typescript.updateImportsOnFileMove.enabled": "always", "eslint.useFlatConfig": true, "githubPullRequests.overrideDefaultBranch": "dev", - "githubIssues.issueBranchTitle": "${issueNumber}-${sanitizedIssueTitle}" + "githubIssues.issueBranchTitle": "${issueNumber}-${sanitizedIssueTitle}", + "cSpell.words": ["Cbor"] } diff --git a/src/background/controller/provider/controller.ts b/src/background/controller/provider/controller.ts index 84b8de67..ca1619b4 100644 --- a/src/background/controller/provider/controller.ts +++ b/src/background/controller/provider/controller.ts @@ -22,8 +22,8 @@ import { storage } from '../../webapi'; import BaseController from '../base'; import Wallet from '../wallet'; -// eslint-disable-next-line import/order,no-restricted-imports -import { signWithKey } from '@/ui/utils/modules/passkey.js'; +// eslint-disable-next-line import/order +import { signWithKey } from '@/background/utils/modules/passkey'; interface Web3WalletPermission { // The name of the method corresponding to the permission diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index afe306ed..64a8d671 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -10,13 +10,12 @@ import { getApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; import web3, { TransactionError } from 'web3'; +import { findAddressWithNetwork } from '@/background/utils/modules/findAddressWithPK'; import eventBus from '@/eventBus'; import { type FeatureFlags } from '@/shared/types/feature-types'; import { type TrackingEvents } from '@/shared/types/tracking-types'; import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; -// eslint-disable-next-line no-restricted-imports -import { findAddressWithNetwork } from '@/ui/utils/modules/findAddressWithPK'; import { keyringService, preferenceService, @@ -60,13 +59,11 @@ import { type EvaluateStorageResult, StorageEvaluator } from '../service/storage import type { UserInfoStore } from '../service/user'; import defaultConfig from '../utils/defaultConfig.json'; import { getStoragedAccount } from '../utils/getStoragedAccount'; +import { pk2PubKey, seed2PubKey, formPubKey } from '../utils/modules/passkey'; import BaseController from './base'; import provider from './provider'; -// eslint-disable-next-line import/order,no-restricted-imports -import { pk2PubKey, seed2PubKey, formPubKey } from '@/ui/utils/modules/passkey.js'; - interface Keyring { type: string; getAccounts(): Promise; @@ -3840,12 +3837,12 @@ export class WalletController extends BaseController { await googleDriveService.uploadMnemonicToGoogleDrive(mnemonic, username, user!.uid, password); mixpanelTrack.track('multi_backup_created', { address: (await this.getCurrentAddress()) || '', - providers: ['google_drive'], + providers: ['GoogleDrive'], }); } catch { mixpanelTrack.track('multi_backup_creation_failed', { address: (await this.getCurrentAddress()) || '', - providers: ['google_drive'], + providers: ['GoogleDrive'], }); } }; @@ -4103,7 +4100,7 @@ export class WalletController extends BaseController { trackAccountRecovered = async () => { mixpanelTrack.track('account_recovered', { address: (await this.getCurrentAddress()) || '', - mechanism: 'multi-backup', + mechanism: 'Multi-Backup', methods: [], }); }; diff --git a/src/background/service/flowns.ts b/src/background/service/flowns.ts index e6d85ea1..729b7296 100644 --- a/src/background/service/flowns.ts +++ b/src/background/service/flowns.ts @@ -1,7 +1,7 @@ import * as secp from '@noble/secp256k1'; import * as fcl from '@onflow/fcl'; -import { signMessageHash } from '@/ui/utils/modules/passkey.js'; +import { signMessageHash } from '@/background/utils/modules/passkey'; import wallet from 'background/controller/wallet'; import { keyringService, openapiService } from 'background/service'; import { createPersistStore } from 'background/utils'; diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index 7eecb693..c5987e6c 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -3,18 +3,16 @@ import * as fcl from '@onflow/fcl'; import { getApp } from 'firebase/app'; import { getAuth, signInAnonymously } from 'firebase/auth'; +import { signWithKey, seed2PubKey } from '@/background/utils/modules/passkey'; import { type ActiveChildType } from '@/shared/types/wallet-types'; import { withPrefix } from '@/shared/utils/address'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; -// eslint-disable-next-line no-restricted-imports -import { findAddressWithSeed, findAddressWithPK } from '@/ui/utils/modules/findAddressWithPK'; -// eslint-disable-next-line no-restricted-imports -import { signWithKey, seed2PubKey } from '@/ui/utils/modules/passkey.js'; import wallet from 'background/controller/wallet'; import { keyringService, mixpanelTrack, openapiService } from 'background/service'; import { createPersistStore } from 'background/utils'; import { getStoragedAccount } from 'background/utils/getStoragedAccount'; +import { findAddressWithSeed, findAddressWithPK } from '../utils/modules/findAddressWithPK'; import { storage } from '../webapi'; import type { diff --git a/src/ui/utils/modules/CborSimpleDecoder.js b/src/background/utils/modules/CborSimpleDecoder.ts similarity index 90% rename from src/ui/utils/modules/CborSimpleDecoder.js rename to src/background/utils/modules/CborSimpleDecoder.ts index e309114c..2086eade 100644 --- a/src/ui/utils/modules/CborSimpleDecoder.js +++ b/src/background/utils/modules/CborSimpleDecoder.ts @@ -1,5 +1,3 @@ -/* eslint-disable no-case-declarations */ -/* eslint-disable @typescript-eslint/ban-ts-comment */ // @ts-nocheck const PositiveInteger = 0; const NegativeInteger = 1; @@ -64,26 +62,26 @@ class Header { } export class CborSimpleDecoder { - static readHeader(reader) { + static readHeader(reader: BinaryReader): Header { if (!(reader instanceof BinaryReader)) throw new TypeError(); const h = reader.readUInt8(); const header = new Header(h); if (header.information >= 0 && header.information <= 23) { header.length = header.information; - } else if (header.information == 24) { + } else if (header.information === 24) { header.length = reader.readUInt8(); - } else if (header.information == 25) { + } else if (header.information === 25) { header.length = reader.readUInt16(); - } else if (header.information == 26) { + } else if (header.information === 26) { header.length = reader.readUInt32(); - } else if (header.information == 27) { + } else if (header.information === 27) { header.length = reader.readUInt64(); } else { throw new Error(`not implemented: major=${header.major} information=${header.information}`); } return header; } - static readObject(reader) { + static readObject(reader: BinaryReader) { if (!(reader instanceof BinaryReader)) throw new TypeError(); const header = CborSimpleDecoder.readHeader(reader); switch (header.major) { diff --git a/src/ui/utils/modules/Crypto.js b/src/background/utils/modules/Crypto.ts similarity index 93% rename from src/ui/utils/modules/Crypto.js rename to src/background/utils/modules/Crypto.ts index 18646920..4c202a04 100644 --- a/src/ui/utils/modules/Crypto.js +++ b/src/background/utils/modules/Crypto.ts @@ -1,6 +1,4 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck -import { encodeArray } from './base64.js'; +import { encodeArray } from './base64'; /** * Convert Cose key to JWK @@ -65,7 +63,7 @@ export function coseToJwk(data) { * @returns {object} - WebCrypto algorithm */ export function getAlgorithm(jwk, alg) { - var algorithm; + let algorithm; switch (jwk.kty) { case 'EC': algorithm = { @@ -81,7 +79,7 @@ export function getAlgorithm(jwk, alg) { default: throw new Error('invalid argument: kty=' + jwk.kty); } - var a = alg ?? jwk.alg ?? 'S256'; + const a = alg ?? jwk.alg ?? 'S256'; switch (a) { case 'RS512': case 'ES512': @@ -118,7 +116,7 @@ export function getAlgorithm(jwk, alg) { * @returns {Promise 32) { b2--; reader.readUInt8(); } const r = reader.readBytes(b2); - if (reader.readUInt8() != 0x02) throw new Error('invalid argument'); + if (reader.readUInt8() !== 0x02) throw new Error('invalid argument'); let b3 = reader.readUInt8(); if (b3 > 32) { b3--; diff --git a/src/ui/utils/modules/WebAuthnDecoder.js b/src/background/utils/modules/WebAuthnDecoder.ts similarity index 63% rename from src/ui/utils/modules/WebAuthnDecoder.js rename to src/background/utils/modules/WebAuthnDecoder.ts index 16d10c6d..a1e99c52 100644 --- a/src/ui/utils/modules/WebAuthnDecoder.js +++ b/src/background/utils/modules/WebAuthnDecoder.ts @@ -1,8 +1,6 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck -import { CborSimpleDecoder, BinaryReader } from './CborSimpleDecoder.js'; -import * as WebAuthn from './WebAuthnTypes.js'; -import { coseToJwk } from './Crypto.js'; +import { CborSimpleDecoder, BinaryReader } from './CborSimpleDecoder'; +import { coseToJwk } from './Crypto'; +import { AttestedCredentialData, AuthenticatorData } from './WebAuthnTypes'; /** * Convert to Uint8Array @@ -24,7 +22,7 @@ export function toUint8Array(data) { * @param {Uint8Array|ArrayBuffer} data * @returns {ArrayBuffer} */ -export function toArrayBuffer(data) { +export function toArrayBuffer(data: Uint8Array | ArrayBuffer): ArrayBuffer { if (data instanceof Uint8Array) { return data.buffer; } @@ -39,7 +37,7 @@ export function toArrayBuffer(data) { * @param {Uint8Array|ArrayBuffer|DataView} data * @returns {DataView} */ -export function toDataView(data) { +export function toDataView(data: DataView | ArrayBuffer | Uint8Array): DataView { if (data instanceof DataView) { return data; } @@ -47,7 +45,7 @@ export function toDataView(data) { return new DataView(data); } if (data instanceof Uint8Array) { - return new DataView(data); + return new DataView(data.buffer); } throw new Error('invalid argument'); } @@ -58,20 +56,47 @@ export function toDataView(data) { * @param {Uint8Array|ArrayBuffer} data * @returns {object} */ -export function decodeClientDataJSON(data) { - data = toUint8Array(data); - return JSON.parse(Array.from(data, (t) => String.fromCharCode(t)).join('')); +export function decodeClientDataJSON(data: ArrayBuffer): object { + const uint8Array = toUint8Array(data); + return JSON.parse(Array.from(uint8Array, (t: number) => String.fromCharCode(t)).join('')); } +export type AttestationFormat = + | 'fido-u2f' + | 'packed' + | 'android-safetynet' + | 'android-key' + | 'tpm' + | 'apple' + | 'none'; + +export type AttestationStatement = { + get(key: 'sig'): Uint8Array | undefined; + get(key: 'x5c'): Uint8Array[] | undefined; + get(key: 'response'): Uint8Array | undefined; + get(key: 'alg'): number | undefined; + get(key: 'ver'): string | undefined; + get(key: 'certInfo'): Uint8Array | undefined; + get(key: 'pubArea'): Uint8Array | undefined; + // `Map` properties + readonly size: number; +}; +export type AttestationObject = { + fmt: AttestationFormat; + attStmt: AttestationStatement; + authData: Uint8Array; +}; + /** * Invokes CborSimpleDecoder.readObject to decode attestationObject * @see https://w3c.github.io/webauthn/#dom-authenticatorattestationresponse-attestationobject * @param {Uint8Array|ArrayBuffer} data * @returns {object} */ -export function decodeAttestationObject(data) { - data = toArrayBuffer(data); - return CborSimpleDecoder.readObject(new BinaryReader(data)); + +export function decodeAttestationObject(data: ArrayBuffer): AttestationObject { + const arrayBuffer = toArrayBuffer(data); + return CborSimpleDecoder.readObject(new BinaryReader(arrayBuffer)); } /** @@ -80,9 +105,9 @@ export function decodeAttestationObject(data) { * @param {Uint8Array|ArrayBuffer} data * @returns {WebAuthn.AuthenticatorData} */ -export function decodeAuthenticatorData(data) { - data = toArrayBuffer(data); - const reader = new BinaryReader(data); +export function decodeAuthenticatorData(data: ArrayBuffer) { + const arrayBuffer = toArrayBuffer(data); + const reader = new BinaryReader(arrayBuffer); /** * https://w3c.github.io/webauthn/#sec-authenticator-data @@ -97,7 +122,7 @@ export function decodeAuthenticatorData(data) { * attestedCredentialData variable * extensions variable */ - const authenticatorData = new WebAuthn.AuthenticatorData(); + const authenticatorData = new AuthenticatorData(); // rpIdHash authenticatorData.rpIdHash = reader.readBytes(32); // flags @@ -115,7 +140,7 @@ export function decodeAuthenticatorData(data) { * credentialId L * credentialPublicKey variable */ - authenticatorData.attestedCredentialData = new WebAuthn.AttestedCredentialData(); + authenticatorData.attestedCredentialData = new AttestedCredentialData(); // aaguid authenticatorData.attestedCredentialData.aaguid = reader.readBytes(16); // credentialIdLength @@ -139,4 +164,4 @@ export function decodeAuthenticatorData(data) { export { coseToJwk, CborSimpleDecoder, BinaryReader }; -export { verifyAssertionSignature } from './Signature.js'; +export { verifyAssertionSignature } from './Signature'; diff --git a/src/ui/utils/modules/WebAuthnTypes.js b/src/background/utils/modules/WebAuthnTypes.ts similarity index 92% rename from src/ui/utils/modules/WebAuthnTypes.js rename to src/background/utils/modules/WebAuthnTypes.ts index f38d5270..67cb9fc0 100644 --- a/src/ui/utils/modules/WebAuthnTypes.js +++ b/src/background/utils/modules/WebAuthnTypes.ts @@ -1,5 +1,3 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck export class PublicKeyCredentialEntity { name; // string constructor(obj) { @@ -9,7 +7,7 @@ export class PublicKeyCredentialEntity { export class PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { id; // string - constructor(obj) { + constructor(obj?: { name: string; id: string }) { super(obj); this.id = obj?.id; } @@ -29,7 +27,7 @@ export class PublicKeyCredentialRpEntity extends PublicKeyCredentialEntity { export class PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { id; // BufferSource displayName; // string - constructor(obj) { + constructor(obj?: { name: string; id: string; displayName: string }) { super(obj); this.displayName = obj?.displayName; } @@ -50,7 +48,7 @@ export class PublicKeyCredentialUserEntity extends PublicKeyCredentialEntity { export class PublicKeyCredentialParameters { type; // string alg; // long - constructor(obj) { + constructor(obj?) { this.type = obj?.type; this.alg = obj?.alg; } @@ -72,7 +70,7 @@ export class PublicKeyCredentialDescriptor { type; // string id; // BufferSource transports; // string[] - constructor(obj) { + constructor(obj?) { this.type = obj?.type; this.id = obj?.id; this.transports = obj?.transports; @@ -97,7 +95,11 @@ export class AuthenticatorSelectionCriteria { residentKey; // string // requireResidentKey // bool userVerification; // string - constructor(obj) { + constructor(obj?: { + authenticatorAttachment: string; + residentKey: string; + userVerification: string; + }) { this.authenticatorAttachment = obj?.authenticatorAttachment; this.residentKey = obj?.residentKey; this.userVerification = obj?.userVerification; @@ -129,7 +131,7 @@ export class PublicKeyCredentialCreationOptions { authenticatorSelection = new AuthenticatorSelectionCriteria(); // AuthenticatorSelectionCriteria attestation; // string extensions; - constructor(obj) { + constructor(obj?) { this.rp = new PublicKeyCredentialRpEntity(obj?.rp); this.user = new PublicKeyCredentialUserEntity(obj?.user); this.challenge = obj?.challenge; @@ -160,7 +162,7 @@ export class PublicKeyCredentialCreationOptions { export class CredentialCreationOptions { signal; // AbortSignal publicKey = new PublicKeyCredentialCreationOptions(); - constructor(obj) { + constructor(obj?) { this.publicKey = new PublicKeyCredentialCreationOptions(obj?.publicKey); } toJSON() { @@ -178,7 +180,7 @@ export class PublicKeyCredentialRequestOptions { allowCredentials; // PublicKeyCredentialDescriptor[] userVerification; // string extensions; - constructor(obj) { + constructor(obj?) { this.challenge = obj?.challenge; this.timeout = obj?.timeout; this.rpId = obj?.rpId; @@ -202,7 +204,7 @@ export class CredentialRequestOptions { mediation; // CredentialMediationRequirement signal; // AbortSignal publicKey = new PublicKeyCredentialRequestOptions(); - constructor(obj) { + constructor(obj?) { this.publicKey = new PublicKeyCredentialRequestOptions(obj?.publicKey); } toJSON() { @@ -217,7 +219,7 @@ export class CredentialRequestOptions { export class Credential { id; // string type; // string - constructor(obj) { + constructor(obj?) { this.id = obj?.id; this.type = obj?.type; } @@ -226,7 +228,7 @@ export class Credential { export class PublicKeyCredential extends Credential { rawId; // ArrayBuffer response; // AuthenticatorResponse (AuthenticatorAttestationResponse or AuthenticatorAssertionResponse) - constructor(obj) { + constructor(obj?) { super(obj); this.rawId = obj?.rawId; if ('attestationObject' in (obj?.response ?? {})) @@ -246,14 +248,14 @@ export class PublicKeyCredential extends Credential { export class AuthenticatorResponse { clientDataJSON; // ArrayBuffer - constructor(obj) { + constructor(obj?) { this.clientDataJSON = obj?.clientDataJSON; } } export class AuthenticatorAttestationResponse extends AuthenticatorResponse { attestationObject; // ArrayBuffer - constructor(obj) { + constructor(obj?) { super(obj); this.attestationObject = obj?.attestationObject; } @@ -269,7 +271,7 @@ export class AuthenticatorAssertionResponse extends AuthenticatorResponse { authenticatorData; // ArrayBuffer signature; // ArrayBuffer userHandle; // ArrayBuffer - constructor(obj) { + constructor(obj?) { super(obj); this.authenticatorData = obj?.authenticatorData; this.signature = obj?.signature; @@ -289,27 +291,27 @@ export class AuthenticatorData { rpIdHash; // ArrayBuffer flags; // int get up() { - return (this.flags & 0x01) != 0; + return (this.flags & 0x01) !== 0; } get uv() { - return (this.flags & 0x04) != 0; + return (this.flags & 0x04) !== 0; } get be() { - return (this.flags & 0x08) != 0; + return (this.flags & 0x08) !== 0; } get bs() { - return (this.flags & 0x10) != 0; + return (this.flags & 0x10) !== 0; } get at() { - return (this.flags & 0x40) != 0; + return (this.flags & 0x40) !== 0; } get ed() { - return (this.flags & 0x80) != 0; + return (this.flags & 0x80) !== 0; } signCount; // int attestedCredentialData; // AttestedCredentialData extensions; // ArrayBuffer - constructor(obj) { + constructor(obj?) { this.rpIdHash = obj?.rpIdHash; this.flags = obj?.flags; this.signCount = obj?.signCount; @@ -339,7 +341,7 @@ export class AttestedCredentialData { aaguid; // ArrayBuffer credentialId; // ArrayBuffer credentialPublicKey; // Jwk - constructor(obj) { + constructor(obj?) { this.aaguid = obj?.aaguid; this.credentialId = obj?.credentialId; this.credentialPublicKey = obj?.credentialPublicKey; diff --git a/src/ui/utils/modules/base64.js b/src/background/utils/modules/base64.ts similarity index 74% rename from src/ui/utils/modules/base64.js rename to src/background/utils/modules/base64.ts index 13deeea0..3474bf45 100644 --- a/src/ui/utils/modules/base64.js +++ b/src/background/utils/modules/base64.ts @@ -1,12 +1,6 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck +import { toUint8Array } from './WebAuthnDecoder'; -import { toUint8Array } from './WebAuthnDecoder.js'; - -function atobUrlSafe(text) { - if (text == null) { - return null; - } +function atobUrlSafe(text: string): string { text = text .replace(/\s+/g, '') // removes whitespace such as linefeeds from input encoded string .replace(/-/g, '+') // replace '-' with '+' @@ -29,10 +23,7 @@ function atobUrlSafe(text) { * https://tools.ietf.org/html/rfc7515#appendix-C * https://tools.ietf.org/html/rfc4648#section-5 */ -function btoaUrlSafe(text) { - if (text == null) { - return null; - } +function btoaUrlSafe(text: string): string { text = btoa(text) .replace(/\+/g, '-') // replace '+' with '-' .replace(/\//g, '_') // replace '/' with '_' @@ -45,9 +36,9 @@ function btoaUrlSafe(text) { * @param {ArrayBuffer|Uint8Array} array - array of bytes * @returns {string} - base64url encoded string */ -function encodeArray(array) { - array = toUint8Array(array); - return btoaUrlSafe(Array.from(array, (t) => String.fromCharCode(t)).join('')); +function encodeArray(array: ArrayBuffer | Uint8Array): string { + const arrayUint8 = toUint8Array(array); + return btoaUrlSafe(Array.from(arrayUint8, (t) => String.fromCharCode(t)).join('')); } /** @@ -55,7 +46,7 @@ function encodeArray(array) { * @param {string} value - base64url encoded string * @returns {Uint8Array} - array of bytes */ -function decodeArray(value) { +function decodeArray(value: string): Uint8Array { if (typeof value !== 'string') { throw new Error('invalid argument'); } diff --git a/src/ui/utils/modules/findAddressWithPK.tsx b/src/background/utils/modules/findAddressWithPK.ts similarity index 100% rename from src/ui/utils/modules/findAddressWithPK.tsx rename to src/background/utils/modules/findAddressWithPK.ts diff --git a/src/ui/utils/modules/findAddressWithPubKey.tsx b/src/background/utils/modules/findAddressWithPubKey.ts similarity index 100% rename from src/ui/utils/modules/findAddressWithPubKey.tsx rename to src/background/utils/modules/findAddressWithPubKey.ts diff --git a/src/ui/utils/modules/passkey.js b/src/background/utils/modules/passkey.ts similarity index 85% rename from src/ui/utils/modules/passkey.js rename to src/background/utils/modules/passkey.ts index 867d3528..8182e338 100644 --- a/src/ui/utils/modules/passkey.js +++ b/src/background/utils/modules/passkey.ts @@ -1,13 +1,16 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck - import { initWasm } from '@trustwallet/wallet-core'; import { getStringFromHashAlgo, getStringFromSignAlgo } from '@/shared/utils/algo'; -import { storage } from 'background/webapi'; -import { decodeArray, encodeArray } from './base64'; -import { FLOW_BIP44_PATH, HASH_ALGO, KEY_TYPE, SIGN_ALGO } from './constants'; +import { + FLOW_BIP44_PATH, + HASH_ALGO, + KEY_TYPE, + SIGN_ALGO, +} from '../../../shared/utils/algo-constants'; +import storage from '../../webapi/storage'; + +import { decodeArray } from './base64'; import { addCredential, readSettings } from './settings'; import { decodeAuthenticatorData, @@ -23,7 +26,7 @@ const jsonToKey = async (json, password) => { const privateKey = PrivateKey.createWithData(privateKeyData); return privateKey; } catch (error) { - console.log(error); + console.error(error); return null; } }; @@ -132,13 +135,13 @@ function getRandomBytes(length) { return array; } -const createPasskey = async (name, displayName) => { +const createPasskey = async (name, displayName, rpName) => { const userId = getRandomBytes(16); - const setup = { + const setup: CredentialCreationOptions = { publicKey: { challenge: getRandomBytes(20), rp: { - name: window.location.hostname, + name: rpName, }, user: { id: userId, @@ -153,15 +156,24 @@ const createPasskey = async (name, displayName) => { ], }, }; + const result = await navigator.credentials.create(setup); console.log('result ==>', result); - const attestationObject = decodeAttestationObject(result.response.attestationObject); + if ( + !result || + !(result instanceof PublicKeyCredential) || + !(result.response instanceof AuthenticatorAttestationResponse) + ) { + return null; + } + const authenticatorResponse: AuthenticatorAttestationResponse = result.response; + const attestationObject = decodeAttestationObject(authenticatorResponse.attestationObject); console.log('attestationObject ==>', attestationObject); const authData = decodeAuthenticatorData(attestationObject.authData); console.log('authData ==>', authData); addCredential( - readSettings(), - setup.publicKey.user, + await readSettings(), + setup.publicKey!.user, result.id, authData.attestedCredentialData.credentialPublicKey, result.response @@ -169,16 +181,16 @@ const createPasskey = async (name, displayName) => { return { userId, result, userName: name }; }; -const getPasskey = async (id) => { - const setup = { +const getPasskey = async (id, rpName) => { + const setup: CredentialRequestOptions = { publicKey: { challenge: getRandomBytes(20), - rpId: window.location.hostname, + rpId: rpName, }, }; if (id && id.length > 0) { - setup.publicKey.allowCredentials = [ + setup.publicKey!.allowCredentials = [ { type: 'public-key', id: decodeArray(id), @@ -188,10 +200,18 @@ const getPasskey = async (id) => { console.log('getPasskey setup ==>', setup); const result = await navigator.credentials.get(setup); + if ( + !result || + !(result instanceof PublicKeyCredential) || + !(result.response instanceof AuthenticatorAssertionResponse) + ) { + return null; + } console.log('getPasskey result ==>', result); const json = decodeClientDataJSON(result.response.clientDataJSON); console.log('clientDataJSON =>', json); - const test = decodeAuthenticatorData(result.response.authenticatorData); + const authenticatorResponse: AuthenticatorAssertionResponse = result.response; + const test = decodeAuthenticatorData(authenticatorResponse.authenticatorData); console.log('authenticatorData =>', test); return result; }; @@ -238,7 +258,7 @@ const getPKfromRegister = async ({ userId, result }) => { }; const uint8Array2Hex = (input) => { - const buffer = new Buffer.from(input); + const buffer = Buffer.from(input); return buffer.toString('hex'); }; diff --git a/src/background/utils/modules/settings.ts b/src/background/utils/modules/settings.ts new file mode 100644 index 00000000..c8dbee6a --- /dev/null +++ b/src/background/utils/modules/settings.ts @@ -0,0 +1,91 @@ +import storage from '../../webapi/storage'; + +import { jsonToString } from './utils'; + +type CredentialSetting = { + instant: string; + user: { + name: string; + displayName: string; + }; + id: string; + credentialPublicKey: Uint8Array; + response: AuthenticatorAttestationResponse; +}; +type Settings = { + credentials: Record; + rp?: Record; + user?: Record; +}; + +export function addCredential( + settings: Settings, + user: PublicKeyCredentialUserEntity, + id: string, + credentialPublicKey: Uint8Array, + response: AuthenticatorAttestationResponse +) { + if (id && id.length > 0) { + settings.credentials[id] = { + instant: new Date().toISOString(), + user: { + name: user.name, + displayName: user.displayName, + }, + id: id, + credentialPublicKey: credentialPublicKey, + response: response, + }; + saveSettings(settings); + } +} + +export function getCredential(settings: Settings, id: string) { + const cred = settings.credentials[id]; + return cred; +} + +export async function getUsername(id: string): Promise { + const settings = await readSettings(); + if (id in settings.credentials) { + const cred = settings.credentials[id]; + return cred.user.name; + } + return null; +} + +export async function readSettings(): Promise { + let settings: Settings = { + credentials: {}, + }; + const s = await storage.get('passkey-settings'); + if (!s) { + return settings; + } + + try { + settings = JSON.parse(s); + + if (!settings.credentials) { + settings.credentials = {}; + } + if ('rp' in settings) { + delete settings.rp; + } + if ('user' in settings) { + delete settings.user; + } + } catch { + console.error('Error parsing settings'); + } + + return settings; +} + +export function saveSettings(settings: Settings) { + if (settings) { + storage.set('passkey-settings', jsonToString(settings)); + } else { + storage.remove('passkey-settings'); + } +} diff --git a/src/background/utils/modules/utils.ts b/src/background/utils/modules/utils.ts new file mode 100644 index 00000000..98204bcb --- /dev/null +++ b/src/background/utils/modules/utils.ts @@ -0,0 +1,15 @@ +import { encodeArray } from './base64'; + +export function replacer(_k: string, v: unknown) { + if (v instanceof ArrayBuffer) { + return encodeArray(v); + } + if (v instanceof Uint8Array) { + return encodeArray(v); + } + return v; +} + +export function jsonToString(obj: unknown) { + return JSON.stringify(obj, replacer, 2); +} diff --git a/src/background/webapi/storage.ts b/src/background/webapi/storage.ts index 588ecc4b..e36905ae 100644 --- a/src/background/webapi/storage.ts +++ b/src/background/webapi/storage.ts @@ -1,10 +1,10 @@ -const get = async (prop?) => { +const get = async (prop: string) => { const result = await chrome.storage.local.get(prop); return prop ? result[prop] : result; }; -const getSession = async (prop?) => { +const getSession = async (prop: string) => { // @ts-ignore const result = await chrome.storage.session?.get(prop); @@ -12,7 +12,7 @@ const getSession = async (prop?) => { return prop ? result[prop] : result; }; -const getExpiry = async (prop?) => { +const getExpiry = async (prop: string) => { const result = await chrome.storage.local.get(prop); const data = result[prop]; @@ -21,16 +21,15 @@ const getExpiry = async (prop?) => { return storageData; }; -const set = async (prop, value): Promise => { - await chrome.storage.local.set({ [prop]: value }); +const set = (prop: string, value: any): Promise => { + return chrome.storage.local.set({ [prop]: value }); }; -const setSession = async (prop, value): Promise => { - // @ts-ignore - await chrome.storage.session?.set({ [prop]: value }); +const setSession = (prop: string, value: any): Promise => { + return chrome.storage.session?.set({ [prop]: value }); }; -const setExpiry = async (prop, value, ttl): Promise => { +const setExpiry = async (prop: string, value: any, ttl: number): Promise => { const now = new Date(); // `item` is an object which contains the original value @@ -41,10 +40,10 @@ const setExpiry = async (prop, value, ttl): Promise => { }; const newValue = JSON.stringify(item); - await chrome.storage.local.set({ [prop]: newValue }); + return await chrome.storage.local.set({ [prop]: newValue }); }; -const checkExpiry = async (value, prop) => { +const checkExpiry = async (value: string, prop: string) => { if (!value) { return null; } @@ -57,14 +56,14 @@ const checkExpiry = async (value, prop) => { if (now.getTime() > item.expiry) { // If the item is expired, delete the item from storage // and return null - chrome.storage.local.remove(prop); + await remove(prop); return null; } return item.value; } catch (error) { console.error('Error parsing storage data', error); try { - chrome.storage.local.remove(prop); + await remove(prop); } catch (error) { console.error('Error removing expired storage data', error); } @@ -72,11 +71,11 @@ const checkExpiry = async (value, prop) => { } }; -const remove = async (prop) => { +const remove = async (prop: string) => { await chrome.storage.local.remove(prop); }; -const removeSession = async (prop) => { +const removeSession = async (prop: string) => { // @ts-ignore await chrome.storage.session?.remove(prop); }; diff --git a/src/shared/types/algo-types.ts b/src/shared/types/algo-types.ts index 707e68de..55e5e9c6 100644 --- a/src/shared/types/algo-types.ts +++ b/src/shared/types/algo-types.ts @@ -1,3 +1,6 @@ export type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1' | 'unknown'; export type HashAlgoType = 'SHA256' | 'SHA2_256' | 'SHA3_256' | 'SHA2_384' | 'SHA3_384' | 'unknown'; +export type KeyType = 'Passkey' | 'GoogleDrive' | 'SeedPhrase' | 'Keystore' | 'PrivateKey'; + +export type RecoveryMechanismType = KeyType | 'Multi-Backup' | 'Device-Backup'; diff --git a/src/shared/types/tracking-types.ts b/src/shared/types/tracking-types.ts index 8f274f42..8885cc2b 100644 --- a/src/shared/types/tracking-types.ts +++ b/src/shared/types/tracking-types.ts @@ -1,16 +1,7 @@ -import type { HashAlgoType, SignAlgoType } from './algo-types'; +import type { HashAlgoType, SignAlgoType, KeyType, RecoveryMechanismType } from './algo-types'; type OnRampSourceType = 'moonpay' | 'coinbase'; -type KeyType = 'passkey' | 'google_drive' | 'seed_phrase' | 'keystore' | 'private_key'; - -type RecoveryMechanismType = - | 'multi-backup' - | 'seed-phrase' - | 'private_key' - | 'KeyStore' - | 'device_backup'; - type AddressType = 'flow' | 'evm' | 'child' | 'coa'; export type TrackingEvents = { diff --git a/src/ui/utils/modules/constants.tsx b/src/shared/utils/algo-constants.ts similarity index 66% rename from src/ui/utils/modules/constants.tsx rename to src/shared/utils/algo-constants.ts index f54ecf8d..3b96acd0 100644 --- a/src/ui/utils/modules/constants.tsx +++ b/src/shared/utils/algo-constants.ts @@ -1,3 +1,5 @@ +import { type SignAlgoType, type HashAlgoType } from '../types/algo-types'; + const FLOW_BIP44_PATH = "m/44'/539'/0'/0/0"; const KEY_TYPE = { @@ -8,12 +10,12 @@ const KEY_TYPE = { PRIVATE_KEY: 'PrivateKey', }; -const SIGN_ALGO = { +const SIGN_ALGO: { [key: string]: SignAlgoType } = { P256: 'ECDSA_P256', SECP256K1: 'ECDSA_secp256k1', }; -const HASH_ALGO = { +const HASH_ALGO: { [key: string]: HashAlgoType } = { SHA256: 'SHA256', SHA3_256: 'SHA3_256', }; diff --git a/src/ui/utils/modules/common.js b/src/ui/utils/modules/common.js deleted file mode 100644 index 76464e48..00000000 --- a/src/ui/utils/modules/common.js +++ /dev/null @@ -1,20 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck -export const invalidArgument = () => { - throw new Error('invalid argument'); -}; -export const isNull = (value) => value === null || value === undefined; -export const notNull = (value) => value !== null && value !== undefined; -export const isEmpty = (value) => isNull(value) || value === ''; -export const notEmpty = (value) => notNull(value) && value !== ''; -export const isString = (value) => notNull(value) && typeof value === 'string'; -export const isFunction = (value) => notNull(value) && typeof value === 'function'; - -export const assertNotNull = (value) => notNull(value) || invalidArgument(); -export const assertString = (value) => isString(value) || invalidArgument(); -export const assertFunction = (value) => isFunction(value) || invalidArgument(); - -export const ifNotNull = (value, func) => - notNull(value) ? (notNull(func) && assertFunction(func) ? func(value) : value) : undefined; -export const ifNotEmpty = (value, func) => - notEmpty(value) ? (notNull(func) && assertFunction(func) ? func(value) : value) : undefined; diff --git a/src/ui/utils/modules/constants.ts b/src/ui/utils/modules/constants.ts new file mode 100644 index 00000000..933bce00 --- /dev/null +++ b/src/ui/utils/modules/constants.ts @@ -0,0 +1 @@ +export * from '@/shared/utils/algo-constants'; diff --git a/src/ui/utils/modules/findAddressWithPK.ts b/src/ui/utils/modules/findAddressWithPK.ts new file mode 100644 index 00000000..e602bbd9 --- /dev/null +++ b/src/ui/utils/modules/findAddressWithPK.ts @@ -0,0 +1,4 @@ +export { + findAddressWithPK, + findAddressWithSeed, +} from '@/background/utils/modules/findAddressWithPK'; diff --git a/src/ui/utils/modules/passkey.ts b/src/ui/utils/modules/passkey.ts new file mode 100644 index 00000000..8aa2221b --- /dev/null +++ b/src/ui/utils/modules/passkey.ts @@ -0,0 +1 @@ +export { jsonToKey } from '@/background/utils/modules/passkey'; diff --git a/src/ui/utils/modules/settings.js b/src/ui/utils/modules/settings.js deleted file mode 100644 index f06e6efe..00000000 --- a/src/ui/utils/modules/settings.js +++ /dev/null @@ -1,99 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck -import { jsonToString } from './utils.js'; -import { notEmpty, notNull } from './common.js'; - -export function createCredentialsList(select, settings, id) { - select.innerHTML = ''; - - let option = document.createElement('option'); - option.setAttribute('value', ''); - option.innerText = ''; - select.appendChild(option); - - let selected = option; - - option = document.createElement('option'); - option.setAttribute('value', '*'); - option.innerText = 'All'; - select.appendChild(option); - - for (const i in settings.credentials) { - const cred = settings.credentials[i]; - //const text = cred.instant + " - " + cred.user.name + " (" + cred.user.displayName + ")"; - const text = `${cred.instant} - ${cred.user.name} (${cred.user.displayName})`; - option = document.createElement('option'); - option.setAttribute('value', cred.id); - option.innerText = text; - select.appendChild(option); - if (notEmpty(id) && cred.id === id) { - selected = option; - } - } - - selected.setAttribute('selected', 'selected'); - - return select; -} - -export function addCredential(settings, user, id, credentialPublicKey, response) { - if (notEmpty(id)) { - settings.credentials[id] = { - instant: new Date().toISOString(), - user: { - name: user.name, - displayName: user.displayName, - }, - id: id, - credentialPublicKey: credentialPublicKey, - response: response, - }; - saveSettings(settings); - } -} - -export function getCredential(settings, id) { - const cred = settings.credentials[id]; - return cred; -} - -export function getUsername(id) { - const seetings = readSettings(); - if (id in seetings.credentials) { - const cred = seetings.credentials[id]; - return cred.user.name; - } - return null; -} - -export function readSettings() { - let settings = { - credentials: {}, - }; - const s = window.localStorage.getItem('settings'); - if (notEmpty(s)) { - try { - settings = JSON.parse(s); - } catch { - console.log('Error parsing settings'); - } - if ('rp' in settings) { - delete settings.rp; - } - if ('user' in settings) { - delete settings.user; - } - if (!('credentials' in settings)) { - settings.credentials = {}; - } - } - return settings; -} - -export function saveSettings(settings) { - if (notNull(settings)) { - window.localStorage.setItem('settings', jsonToString(settings)); - } else { - window.localStorage.removeItem('settings'); - } -} diff --git a/src/ui/utils/modules/utils.js b/src/ui/utils/modules/utils.js deleted file mode 100644 index 392cf65b..00000000 --- a/src/ui/utils/modules/utils.js +++ /dev/null @@ -1,27 +0,0 @@ -/* eslint-disable @typescript-eslint/ban-ts-comment */ -// @ts-nocheck -import { encodeArray } from './base64.js'; - -export function replacer(k, v) { - if (v instanceof ArrayBuffer) { - return encodeArray(v); - } - if (v instanceof Uint8Array) { - return encodeArray(v); - } - return v; -} - -export function jsonToString(obj) { - return JSON.stringify(obj, replacer, 2); -} - -export function toggle_section(id, visible) { - const section = document.getElementById(id); - section.querySelector(":scope > input[type='checkbox']").checked = visible == false; -} - -export function clear_error(id) { - const section = document.getElementById(id); - section.querySelectorAll('.error').forEach((e) => e.classList.remove('error')); -} diff --git a/tsconfig.json b/tsconfig.json index b0b1a583..131e6730 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -36,6 +36,6 @@ ], "outDir": "./dist" }, - "exclude": ["./node_modules", "src/ui/utils/modules"], + "exclude": ["./node_modules"], "include": ["src", "__tests__"] } From 9c26934b2e7cab04816078426833028be0978b29 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 10:59:01 +1100 Subject: [PATCH 2/7] Renamed passkey to publicPrivateKey as it was confusing Closes #298 --- src/background/controller/provider/controller.ts | 2 +- src/background/controller/wallet.ts | 2 +- src/background/index.ts | 4 ++-- src/background/service/flowns.ts | 2 +- src/background/service/userWallet.ts | 2 +- src/background/utils/modules/findAddressWithPK.ts | 2 +- .../utils/modules/{passkey.ts => publicPrivateKey.ts} | 0 src/ui/utils/modules/passkey.ts | 2 +- 8 files changed, 8 insertions(+), 8 deletions(-) rename src/background/utils/modules/{passkey.ts => publicPrivateKey.ts} (100%) diff --git a/src/background/controller/provider/controller.ts b/src/background/controller/provider/controller.ts index ca1619b4..4e90bded 100644 --- a/src/background/controller/provider/controller.ts +++ b/src/background/controller/provider/controller.ts @@ -23,7 +23,7 @@ import BaseController from '../base'; import Wallet from '../wallet'; // eslint-disable-next-line import/order -import { signWithKey } from '@/background/utils/modules/passkey'; +import { signWithKey } from '@/background/utils/modules/publicPrivateKey'; interface Web3WalletPermission { // The name of the method corresponding to the permission diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 64a8d671..e3d9f491 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -59,7 +59,7 @@ import { type EvaluateStorageResult, StorageEvaluator } from '../service/storage import type { UserInfoStore } from '../service/user'; import defaultConfig from '../utils/defaultConfig.json'; import { getStoragedAccount } from '../utils/getStoragedAccount'; -import { pk2PubKey, seed2PubKey, formPubKey } from '../utils/modules/passkey'; +import { pk2PubKey, seed2PubKey, formPubKey } from '../utils/modules/publicPrivateKey'; import BaseController from './base'; import provider from './provider'; diff --git a/src/background/index.ts b/src/background/index.ts index 06710139..98c5c6b0 100644 --- a/src/background/index.ts +++ b/src/background/index.ts @@ -147,7 +147,7 @@ async function restoreAppState() { restoreAppState(); -chrome.runtime.onInstalled.addListener(({ reason }) => { +chrome.runtime.onInstalled.addListener(({ reason }: chrome.runtime.InstalledDetails) => { // chrome.runtime.OnInstalledReason.Install if (reason === 'install') { chrome.tabs.create({ @@ -179,7 +179,7 @@ chrome.runtime.onMessage.addListener(function (request, sender, sendResponse) { }); // for page provider -chrome.runtime.onConnect.addListener((port) => { +chrome.runtime.onConnect.addListener((port: chrome.runtime.Port) => { // openapiService.getConfig(); // @ts-ignore diff --git a/src/background/service/flowns.ts b/src/background/service/flowns.ts index 729b7296..4eef2100 100644 --- a/src/background/service/flowns.ts +++ b/src/background/service/flowns.ts @@ -1,7 +1,7 @@ import * as secp from '@noble/secp256k1'; import * as fcl from '@onflow/fcl'; -import { signMessageHash } from '@/background/utils/modules/passkey'; +import { signMessageHash } from '@/background/utils/modules/publicPrivateKey'; import wallet from 'background/controller/wallet'; import { keyringService, openapiService } from 'background/service'; import { createPersistStore } from 'background/utils'; diff --git a/src/background/service/userWallet.ts b/src/background/service/userWallet.ts index c5987e6c..9114ad3f 100644 --- a/src/background/service/userWallet.ts +++ b/src/background/service/userWallet.ts @@ -3,7 +3,7 @@ import * as fcl from '@onflow/fcl'; import { getApp } from 'firebase/app'; import { getAuth, signInAnonymously } from 'firebase/auth'; -import { signWithKey, seed2PubKey } from '@/background/utils/modules/passkey'; +import { signWithKey, seed2PubKey } from '@/background/utils/modules/publicPrivateKey'; import { type ActiveChildType } from '@/shared/types/wallet-types'; import { withPrefix } from '@/shared/utils/address'; import { getHashAlgo, getSignAlgo } from '@/shared/utils/algo'; diff --git a/src/background/utils/modules/findAddressWithPK.ts b/src/background/utils/modules/findAddressWithPK.ts index dd24750c..a097e962 100644 --- a/src/background/utils/modules/findAddressWithPK.ts +++ b/src/background/utils/modules/findAddressWithPK.ts @@ -1,5 +1,5 @@ import { findAddressWithKey, findAddressOnlyKey } from './findAddressWithPubKey'; -import { pk2PubKey, seed2PubKey, seed2PubKeyTemp } from './passkey'; +import { pk2PubKey, seed2PubKey, seed2PubKeyTemp } from './publicPrivateKey'; export const findAddress = async (pubKTuple, address) => { const { P256, SECP256K1 } = pubKTuple; diff --git a/src/background/utils/modules/passkey.ts b/src/background/utils/modules/publicPrivateKey.ts similarity index 100% rename from src/background/utils/modules/passkey.ts rename to src/background/utils/modules/publicPrivateKey.ts diff --git a/src/ui/utils/modules/passkey.ts b/src/ui/utils/modules/passkey.ts index 8aa2221b..d04974c0 100644 --- a/src/ui/utils/modules/passkey.ts +++ b/src/ui/utils/modules/passkey.ts @@ -1 +1 @@ -export { jsonToKey } from '@/background/utils/modules/passkey'; +export { jsonToKey } from '@/background/utils/modules/publicPrivateKey'; From 1f174cf3b535e42aaf99214635524c1d6ef35c24 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:14:21 +1100 Subject: [PATCH 3/7] Separated passkey out. Added wallet functions for pk functions called from UI Closes #298 --- .vscode/settings.json | 2 +- src/background/controller/wallet.ts | 22 ++- src/background/utils/modules/passkey.ts | 143 +++++++++++++++ .../utils/modules/publicPrivateKey.ts | 172 +----------------- 4 files changed, 174 insertions(+), 165 deletions(-) create mode 100644 src/background/utils/modules/passkey.ts diff --git a/.vscode/settings.json b/.vscode/settings.json index 91342cd5..d6093fce 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -22,5 +22,5 @@ "eslint.useFlatConfig": true, "githubPullRequests.overrideDefaultBranch": "dev", "githubIssues.issueBranchTitle": "${issueNumber}-${sanitizedIssueTitle}", - "cSpell.words": ["Cbor"] + "cSpell.words": ["Cbor", "secp"] } diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index e3d9f491..986003ef 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -10,7 +10,17 @@ import { getApp } from 'firebase/app'; import { getAuth } from 'firebase/auth'; import web3, { TransactionError } from 'web3'; -import { findAddressWithNetwork } from '@/background/utils/modules/findAddressWithPK'; +import { + findAddressWithNetwork, + findAddressWithSeed, + findAddressWithPK, +} from '@/background/utils/modules/findAddressWithPK'; +import { + pk2PubKey, + seed2PubKey, + formPubKey, + jsonToKey, +} from '@/background/utils/modules/publicPrivateKey'; import eventBus from '@/eventBus'; import { type FeatureFlags } from '@/shared/types/feature-types'; import { type TrackingEvents } from '@/shared/types/tracking-types'; @@ -59,7 +69,6 @@ import { type EvaluateStorageResult, StorageEvaluator } from '../service/storage import type { UserInfoStore } from '../service/user'; import defaultConfig from '../utils/defaultConfig.json'; import { getStoragedAccount } from '../utils/getStoragedAccount'; -import { pk2PubKey, seed2PubKey, formPubKey } from '../utils/modules/publicPrivateKey'; import BaseController from './base'; import provider from './provider'; @@ -509,6 +518,15 @@ export class WalletController extends BaseController { return this._setCurrentAccountFromKeyring(keyring); }; + jsonToPrivateKey = (json: string, password: string) => { + return jsonToKey(json, password); + }; + findAddressWithPrivateKey = (pk: string, address: string) => { + return findAddressWithPK(pk, address); + }; + findAddressWithSeedPhrase = (seed: string, address: string) => { + return findAddressWithSeed(seed, address); + }; getPreMnemonics = () => keyringService.getPreMnemonics(); generatePreMnemonic = () => keyringService.generatePreMnemonic(); removePreMnemonics = () => keyringService.removePreMnemonics(); diff --git a/src/background/utils/modules/passkey.ts b/src/background/utils/modules/passkey.ts new file mode 100644 index 00000000..33be1e0f --- /dev/null +++ b/src/background/utils/modules/passkey.ts @@ -0,0 +1,143 @@ +import { initWasm } from '@trustwallet/wallet-core'; + +import { + FLOW_BIP44_PATH, + HASH_ALGO, + KEY_TYPE, + SIGN_ALGO, +} from '../../../shared/utils/algo-constants'; + +import { decodeArray } from './base64'; +import { addCredential, readSettings } from './settings'; +import { + decodeAuthenticatorData, + decodeClientDataJSON, + decodeAttestationObject, +} from './WebAuthnDecoder'; + +function getRandomBytes(length) { + const array = new Uint8Array(length ?? 32); + crypto.getRandomValues(array); + return array; +} + +const createPasskey = async (name, displayName, rpName) => { + const userId = getRandomBytes(16); + const setup: CredentialCreationOptions = { + publicKey: { + challenge: getRandomBytes(20), + rp: { + name: rpName, + }, + user: { + id: userId, + name: name, + displayName: displayName, + }, + pubKeyCredParams: [ + { + type: 'public-key', + alg: -7, + }, + ], + }, + }; + + const result = await navigator.credentials.create(setup); + if ( + !result || + !(result instanceof PublicKeyCredential) || + !(result.response instanceof AuthenticatorAttestationResponse) + ) { + return null; + } + const authenticatorResponse: AuthenticatorAttestationResponse = result.response; + const attestationObject = decodeAttestationObject(authenticatorResponse.attestationObject); + const authData = decodeAuthenticatorData(attestationObject.authData); + addCredential( + await readSettings(), + setup.publicKey!.user, + result.id, + authData.attestedCredentialData.credentialPublicKey, + result.response + ); + return { userId, result, userName: name }; +}; + +const getPasskey = async (id, rpName) => { + const setup: CredentialRequestOptions = { + publicKey: { + challenge: getRandomBytes(20), + rpId: rpName, + }, + }; + + if (id && id.length > 0) { + setup.publicKey!.allowCredentials = [ + { + type: 'public-key', + id: decodeArray(id), + }, + ]; + } + + const result = await navigator.credentials.get(setup); + if ( + !result || + !(result instanceof PublicKeyCredential) || + !(result.response instanceof AuthenticatorAssertionResponse) + ) { + return null; + } + const json = decodeClientDataJSON(result.response.clientDataJSON); + const authenticatorResponse: AuthenticatorAssertionResponse = result.response; + const test = decodeAuthenticatorData(authenticatorResponse.authenticatorData); + return result; +}; + +const getPKfromLogin = async (result) => { + const { HDWallet, Curve } = await initWasm(); + const wallet = HDWallet.createWithEntropy(result.response.userHandle, ''); + const pk = wallet.getKeyByCurve(Curve.nist256p1, FLOW_BIP44_PATH); + const pubk = pk.getPublicKeyNist256p1().uncompressed().data(); + const json = decodeClientDataJSON(result.response.clientDataJSON); + + return { + mnemonic: wallet.mnemonic(), + type: KEY_TYPE.PASSKEY, + pk: uint8Array2Hex(pk.data()), + pubK: uint8Array2Hex(pubk).replace(/^04/, ''), + keyIndex: 0, + signAlgo: SIGN_ALGO.P256, + hashAlgo: HASH_ALGO.SHA256, + addtional: { + clientDataJSON: json, + }, + }; +}; + +const getPKfromRegister = async ({ userId, result }) => { + if (!userId) { + return null; + } + const { HDWallet, Curve } = await initWasm(); + const wallet = HDWallet.createWithEntropy(userId, ''); + const pk = wallet.getKeyByCurve(Curve.nist256p1, FLOW_BIP44_PATH); + const pubk = pk.getPublicKeyNist256p1().uncompressed().data(); + return { + type: KEY_TYPE.PASSKEY, + mnemonic: wallet.mnemonic(), + pk: uint8Array2Hex(pk.data()), + pubK: uint8Array2Hex(pubk).replace(/^04/, ''), + keyIndex: 0, + signAlgo: SIGN_ALGO.P256, + hashAlgo: HASH_ALGO.SHA256, + }; +}; + +const uint8Array2Hex = (input) => { + const buffer = Buffer.from(input); + return buffer.toString('hex'); +}; + +export { createPasskey, getPasskey, getPKfromLogin, getPKfromRegister }; diff --git a/src/background/utils/modules/publicPrivateKey.ts b/src/background/utils/modules/publicPrivateKey.ts index 8182e338..2d19250f 100644 --- a/src/background/utils/modules/publicPrivateKey.ts +++ b/src/background/utils/modules/publicPrivateKey.ts @@ -2,27 +2,16 @@ import { initWasm } from '@trustwallet/wallet-core'; import { getStringFromHashAlgo, getStringFromSignAlgo } from '@/shared/utils/algo'; -import { - FLOW_BIP44_PATH, - HASH_ALGO, - KEY_TYPE, - SIGN_ALGO, -} from '../../../shared/utils/algo-constants'; +import { FLOW_BIP44_PATH, HASH_ALGO, SIGN_ALGO } from '../../../shared/utils/algo-constants'; import storage from '../../webapi/storage'; -import { decodeArray } from './base64'; -import { addCredential, readSettings } from './settings'; -import { - decodeAuthenticatorData, - decodeClientDataJSON, - decodeAttestationObject, -} from './WebAuthnDecoder'; - -const jsonToKey = async (json, password) => { +const jsonToKey = async (json: string, password: string) => { try { const { StoredKey, PrivateKey } = await initWasm(); - const keystore = StoredKey.importJSON(json); - const privateKeyData = await keystore.decryptPrivateKey(password); + // It appears StoredKey.importJSON expects a Buffer, not a string + const jsonBuffer = Buffer.from(json); + const keystore = StoredKey.importJSON(jsonBuffer); + const privateKeyData = keystore.decryptPrivateKey(Buffer.from(password)); const privateKey = PrivateKey.createWithData(privateKeyData); return privateKey; } catch (error) { @@ -31,7 +20,7 @@ const jsonToKey = async (json, password) => { } }; -const pk2PubKey = async (pk) => { +const pk2PubKey = async (pk: string) => { const { PrivateKey } = await initWasm(); const privateKey = PrivateKey.createWithData(Buffer.from(pk, 'hex')); @@ -64,7 +53,7 @@ const formPubKey = async (pubKey) => { }; }; -const seed2PubKey = async (seed) => { +const seed2PubKey = async (seed: string) => { const { HDWallet, Curve } = await initWasm(); const currentId = (await storage.get('currentId')) ?? 0; @@ -101,13 +90,11 @@ const seed2PubKey = async (seed) => { }; }; -const seed2PubKeyTemp = async (seed) => { +const seed2PubKeyTemp = async (seed: string) => { const { HDWallet, Curve } = await initWasm(); const path = (await storage.get('temp_path')) || FLOW_BIP44_PATH; const passphrase = (await storage.get('temp_phrase')) || ''; - console.log('pathpathpath ', path); - console.log('passphrase ', passphrase); const wallet = HDWallet.createWithMnemonic(seed, passphrase); const p256PK = wallet.getKeyByCurve(Curve.nist256p1, path); const p256PubK = Buffer.from(p256PK.getPublicKeyNist256p1().uncompressed().data()) @@ -129,139 +116,6 @@ const seed2PubKeyTemp = async (seed) => { }; }; -function getRandomBytes(length) { - const array = new Uint8Array(length ?? 32); - crypto.getRandomValues(array); - return array; -} - -const createPasskey = async (name, displayName, rpName) => { - const userId = getRandomBytes(16); - const setup: CredentialCreationOptions = { - publicKey: { - challenge: getRandomBytes(20), - rp: { - name: rpName, - }, - user: { - id: userId, - name: name, - displayName: displayName, - }, - pubKeyCredParams: [ - { - type: 'public-key', - alg: -7, - }, - ], - }, - }; - - const result = await navigator.credentials.create(setup); - console.log('result ==>', result); - if ( - !result || - !(result instanceof PublicKeyCredential) || - !(result.response instanceof AuthenticatorAttestationResponse) - ) { - return null; - } - const authenticatorResponse: AuthenticatorAttestationResponse = result.response; - const attestationObject = decodeAttestationObject(authenticatorResponse.attestationObject); - console.log('attestationObject ==>', attestationObject); - const authData = decodeAuthenticatorData(attestationObject.authData); - console.log('authData ==>', authData); - addCredential( - await readSettings(), - setup.publicKey!.user, - result.id, - authData.attestedCredentialData.credentialPublicKey, - result.response - ); - return { userId, result, userName: name }; -}; - -const getPasskey = async (id, rpName) => { - const setup: CredentialRequestOptions = { - publicKey: { - challenge: getRandomBytes(20), - rpId: rpName, - }, - }; - - if (id && id.length > 0) { - setup.publicKey!.allowCredentials = [ - { - type: 'public-key', - id: decodeArray(id), - }, - ]; - } - - console.log('getPasskey setup ==>', setup); - const result = await navigator.credentials.get(setup); - if ( - !result || - !(result instanceof PublicKeyCredential) || - !(result.response instanceof AuthenticatorAssertionResponse) - ) { - return null; - } - console.log('getPasskey result ==>', result); - const json = decodeClientDataJSON(result.response.clientDataJSON); - console.log('clientDataJSON =>', json); - const authenticatorResponse: AuthenticatorAssertionResponse = result.response; - const test = decodeAuthenticatorData(authenticatorResponse.authenticatorData); - console.log('authenticatorData =>', test); - return result; -}; - -const getPKfromLogin = async (result) => { - const { HDWallet, Curve } = await initWasm(); - const wallet = HDWallet.createWithEntropy(result.response.userHandle, ''); - const pk = wallet.getKeyByCurve(Curve.nist256p1, FLOW_BIP44_PATH); - const pubk = pk.getPublicKeyNist256p1().uncompressed().data(); - const json = decodeClientDataJSON(result.response.clientDataJSON); - - return { - mnemonic: wallet.mnemonic(), - type: KEY_TYPE.PASSKEY, - pk: uint8Array2Hex(pk.data()), - pubK: uint8Array2Hex(pubk).replace(/^04/, ''), - keyIndex: 0, - signAlgo: SIGN_ALGO.P256, - hashAlgo: HASH_ALGO.SHA256, - addtional: { - clientDataJSON: json, - }, - }; -}; - -const getPKfromRegister = async ({ userId, result }) => { - console.log(userId, result); - if (!userId) { - return null; - } - const { HDWallet, Curve } = await initWasm(); - const wallet = HDWallet.createWithEntropy(userId, ''); - const pk = wallet.getKeyByCurve(Curve.nist256p1, FLOW_BIP44_PATH); - const pubk = pk.getPublicKeyNist256p1().uncompressed().data(); - return { - type: KEY_TYPE.PASSKEY, - mnemonic: wallet.mnemonic(), - pk: uint8Array2Hex(pk.data()), - pubK: uint8Array2Hex(pubk).replace(/^04/, ''), - keyIndex: 0, - signAlgo: SIGN_ALGO.P256, - hashAlgo: HASH_ALGO.SHA256, - }; -}; - -const uint8Array2Hex = (input) => { - const buffer = Buffer.from(input); - return buffer.toString('hex'); -}; - const signMessageHash = async (hashAlgo, messageData) => { // Other key const { Hash } = await initWasm(); @@ -278,9 +132,7 @@ const signWithKey = async (message, signAlgo, hashAlgo, pk) => { if (typeof hashAlgo === 'number') { hashAlgo = getStringFromHashAlgo(hashAlgo); } - console.log(' signAlgo ', signAlgo); - console.log(' hashAlgo ', hashAlgo); - const { HDWallet, Curve, Hash, PrivateKey } = await initWasm(); + const { Curve, Hash, PrivateKey } = await initWasm(); const messageData = Buffer.from(message, 'hex'); const privateKey = PrivateKey.createWithData(Buffer.from(pk, 'hex')); const curve = signAlgo === SIGN_ALGO.P256 ? Curve.nist256p1 : Curve.secp256k1; @@ -291,10 +143,6 @@ const signWithKey = async (message, signAlgo, hashAlgo, pk) => { }; export { - createPasskey, - getPasskey, - getPKfromLogin, - getPKfromRegister, jsonToKey, pk2PubKey, seed2PubKey, From d895b0e118edc10753331d692cbeaeec4d95c50b Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 12:32:53 +1100 Subject: [PATCH 4/7] Updated imports to use wallet Closes #298 --- src/background/controller/wallet.ts | 4 ++-- src/shared/types/algo-types.ts | 4 ++-- src/shared/types/tracking-types.ts | 15 ++++++++----- src/shared/utils/algo-constants.ts | 4 ++-- src/ui/utils/modules/findAddressWithPK.ts | 4 ---- src/ui/utils/modules/passkey.ts | 1 - .../importComponent/JsonImport.tsx | 22 +++++++++---------- .../importComponent/KeyImport.tsx | 12 +++++----- .../importComponent/SeedPhrase.tsx | 12 +++++----- .../importComponent/JsonImport.tsx | 22 +++++++++---------- .../importComponent/KeyImport.tsx | 12 +++++----- .../importComponent/SeedPhrase.tsx | 13 ++++++----- 12 files changed, 65 insertions(+), 60 deletions(-) delete mode 100644 src/ui/utils/modules/findAddressWithPK.ts delete mode 100644 src/ui/utils/modules/passkey.ts diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 986003ef..31e5733f 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -524,8 +524,8 @@ export class WalletController extends BaseController { findAddressWithPrivateKey = (pk: string, address: string) => { return findAddressWithPK(pk, address); }; - findAddressWithSeedPhrase = (seed: string, address: string) => { - return findAddressWithSeed(seed, address); + findAddressWithSeedPhrase = (seed: string, address: string, isTemp: boolean = false) => { + return findAddressWithSeed(seed, address, isTemp); }; getPreMnemonics = () => keyringService.getPreMnemonics(); generatePreMnemonic = () => keyringService.generatePreMnemonic(); diff --git a/src/shared/types/algo-types.ts b/src/shared/types/algo-types.ts index 55e5e9c6..4953e5b4 100644 --- a/src/shared/types/algo-types.ts +++ b/src/shared/types/algo-types.ts @@ -1,6 +1,6 @@ export type SignAlgoType = 'ECDSA_P256' | 'ECDSA_secp256k1' | 'unknown'; export type HashAlgoType = 'SHA256' | 'SHA2_256' | 'SHA3_256' | 'SHA2_384' | 'SHA3_384' | 'unknown'; -export type KeyType = 'Passkey' | 'GoogleDrive' | 'SeedPhrase' | 'Keystore' | 'PrivateKey'; +export type ImportKeyType = 'Passkey' | 'GoogleDrive' | 'SeedPhrase' | 'Keystore' | 'PrivateKey'; -export type RecoveryMechanismType = KeyType | 'Multi-Backup' | 'Device-Backup'; +export type RecoveryMechanismType = ImportKeyType | 'Multi-Backup' | 'Device-Backup'; diff --git a/src/shared/types/tracking-types.ts b/src/shared/types/tracking-types.ts index 8885cc2b..f0f1f512 100644 --- a/src/shared/types/tracking-types.ts +++ b/src/shared/types/tracking-types.ts @@ -1,4 +1,9 @@ -import type { HashAlgoType, SignAlgoType, KeyType, RecoveryMechanismType } from './algo-types'; +import type { + HashAlgoType, + SignAlgoType, + ImportKeyType, + RecoveryMechanismType, +} from './algo-types'; type OnRampSourceType = 'moonpay' | 'coinbase'; @@ -42,11 +47,11 @@ export type TrackingEvents = { // Backup Events multi_backup_created: { address: string; // Address of the account that set up multi-backup - providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase + providers: ImportKeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase }; multi_backup_creation_failed: { address: string; // Address of the account that set up multi-backup - providers: KeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase + providers: ImportKeyType[]; // Providers used in the multi-backup, GoogleDrive, iCloud, Seed e.g. google_drive icloud seed_phrase }; // Transaction Events @@ -90,7 +95,7 @@ export type TrackingEvents = { // Account Events account_created: { public_key: string; // The public key used for creating the new account - key_type?: KeyType; // The key type of the account (if available) + key_type?: ImportKeyType; // The key type of the account (if available) sign_algo: SignAlgoType; // Signature algorithm of the key hash_algo: HashAlgoType; // Hash algo Hash algorithm of the key }; @@ -101,7 +106,7 @@ export type TrackingEvents = { account_recovered: { address: string; // Address that was recovered mechanism: RecoveryMechanismType; // The way the account was recovered - methods: KeyType[]; // Array of providers used in the multi-backup, GoogleDrive, iCloud, Seed + methods: ImportKeyType[]; // Array of providers used in the multi-backup, GoogleDrive, iCloud, Seed }; }; diff --git a/src/shared/utils/algo-constants.ts b/src/shared/utils/algo-constants.ts index 3b96acd0..cc6f1385 100644 --- a/src/shared/utils/algo-constants.ts +++ b/src/shared/utils/algo-constants.ts @@ -1,8 +1,8 @@ -import { type SignAlgoType, type HashAlgoType } from '../types/algo-types'; +import { type SignAlgoType, type HashAlgoType, type ImportKeyType } from '../types/algo-types'; const FLOW_BIP44_PATH = "m/44'/539'/0'/0/0"; -const KEY_TYPE = { +const KEY_TYPE: { [key: string]: ImportKeyType } = { PASSKEY: 'Passkey', GOOGLE_DRIVE: 'GoogleDrive', SEED_PHRASE: 'SeedPhrase', diff --git a/src/ui/utils/modules/findAddressWithPK.ts b/src/ui/utils/modules/findAddressWithPK.ts deleted file mode 100644 index e602bbd9..00000000 --- a/src/ui/utils/modules/findAddressWithPK.ts +++ /dev/null @@ -1,4 +0,0 @@ -export { - findAddressWithPK, - findAddressWithSeed, -} from '@/background/utils/modules/findAddressWithPK'; diff --git a/src/ui/utils/modules/passkey.ts b/src/ui/utils/modules/passkey.ts deleted file mode 100644 index d04974c0..00000000 --- a/src/ui/utils/modules/passkey.ts +++ /dev/null @@ -1 +0,0 @@ -export { jsonToKey } from '@/background/utils/modules/publicPrivateKey'; diff --git a/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx b/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx index 9ce5a690..181b32f8 100644 --- a/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx +++ b/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx @@ -1,7 +1,4 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithPK } from '../../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../../utils/modules/constants'; -import React from 'react'; +import { Visibility, VisibilityOff } from '@mui/icons-material'; import { Box, Button, @@ -11,11 +8,14 @@ import { TextareaAutosize, InputAdornment, } from '@mui/material'; -import { Visibility, VisibilityOff } from '@mui/icons-material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { KEY_TYPE } from '@/shared/utils/algo-constants'; +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; + import ErrorModel from '../../../../FRWComponent/PopupModal/errorModel'; -import { jsonToKey } from '../../../../utils/modules/passkey'; const useStyles = makeStyles((theme) => ({ form: { @@ -61,6 +61,7 @@ const useStyles = makeStyles((theme) => ({ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { const classes = useStyles(); + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const [isInvalid, setIsInvalid] = useState(false); const [json, setJson] = useState(''); @@ -91,14 +92,13 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { } const password = e.target[2].value; const address = e.target[5].value; - const pk = await jsonToKey(keystore, password); - if (pk == null) { + const pk = await wallet.jsonToPrivateKey(keystore, password); + if (pk === null) { setErrorMessage('Password incorrect'); return; } const pkHex = Buffer.from(pk.data()).toString('hex'); - const result = await findAddressWithPK(pkHex, address); - console.log(result); + const result = await wallet.findAddressWithPrivateKey(pkHex, address); setPk(pkHex); if (!result) { onOpen(); @@ -180,7 +180,7 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { - {errorMesssage != '' && ( + {errorMesssage !== '' && ( { diff --git a/src/ui/views/AddWelcome/AddressImport/importComponent/KeyImport.tsx b/src/ui/views/AddWelcome/AddressImport/importComponent/KeyImport.tsx index 5111453a..d41fbd2e 100644 --- a/src/ui/views/AddWelcome/AddressImport/importComponent/KeyImport.tsx +++ b/src/ui/views/AddWelcome/AddressImport/importComponent/KeyImport.tsx @@ -1,9 +1,9 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithPK } from '../../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../../utils/modules/constants'; -import React from 'react'; import { Box, Button, Typography, TextField, TextareaAutosize } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { KEY_TYPE } from '@/shared/utils/algo-constants'; +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; const useStyles = makeStyles((theme) => ({ @@ -31,7 +31,7 @@ const useStyles = makeStyles((theme) => ({ const KeyImport = ({ onOpen, onImport, setPk, isSignLoading }) => { // const classes = useStyles(); const classes = useStyles(); - + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const handleImport = async (e) => { @@ -43,7 +43,7 @@ const KeyImport = ({ onOpen, onImport, setPk, isSignLoading }) => { const inputValue = e.target[2].value; setPk(pk); const address = flowAddressRegex.test(inputValue) ? inputValue : null; - const result = await findAddressWithPK(pk, address); + const result = await wallet.findAddressWithPrivateKey(pk, address); if (!result) { onOpen(); return; diff --git a/src/ui/views/AddWelcome/AddressImport/importComponent/SeedPhrase.tsx b/src/ui/views/AddWelcome/AddressImport/importComponent/SeedPhrase.tsx index 75d1b6d3..7ef03300 100644 --- a/src/ui/views/AddWelcome/AddressImport/importComponent/SeedPhrase.tsx +++ b/src/ui/views/AddWelcome/AddressImport/importComponent/SeedPhrase.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithSeed } from '../../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../../utils/modules/constants'; -import React from 'react'; import { Box, Button, Typography, TextField, TextareaAutosize } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; + import KeyPathInput from '../../../../FRWComponent/KeyPathInputs'; +import { KEY_TYPE } from '../../../../utils/modules/constants'; const useStyles = makeStyles((theme) => ({ form: { @@ -32,6 +33,7 @@ const useStyles = makeStyles((theme) => ({ const SeedPhraseImport = ({ onOpen, onImport, setmnemonic, isSignLoading }) => { const classes = useStyles(); + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const handleImport = async (e) => { @@ -44,7 +46,7 @@ const SeedPhraseImport = ({ onOpen, onImport, setmnemonic, isSignLoading }) => { const inputValue = e.target[2].value; const address = flowAddressRegex.test(inputValue) ? inputValue : null; - const result = await findAddressWithSeed(seed, address, true); + const result = await wallet.findAddressWithSeedPhrase(seed, address, true); if (!result) { onOpen(); return; diff --git a/src/ui/views/AddressImport/importComponent/JsonImport.tsx b/src/ui/views/AddressImport/importComponent/JsonImport.tsx index 09b8499f..fb161277 100644 --- a/src/ui/views/AddressImport/importComponent/JsonImport.tsx +++ b/src/ui/views/AddressImport/importComponent/JsonImport.tsx @@ -1,7 +1,4 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithPK } from '../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../utils/modules/constants'; -import React from 'react'; +import { Visibility, VisibilityOff } from '@mui/icons-material'; import { Box, Button, @@ -11,11 +8,14 @@ import { TextareaAutosize, InputAdornment, } from '@mui/material'; -import { Visibility, VisibilityOff } from '@mui/icons-material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; -import { jsonToKey } from '../../../utils/modules/passkey'; + import ErrorModel from '../../../FRWComponent/PopupModal/errorModel'; +import { KEY_TYPE } from '../../../utils/modules/constants'; const useStyles = makeStyles((theme) => ({ form: { @@ -61,6 +61,7 @@ const useStyles = makeStyles((theme) => ({ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { const classes = useStyles(); + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const [isInvalid, setIsInvalid] = useState(false); const [json, setJson] = useState(''); @@ -91,14 +92,13 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { } const password = e.target[2].value; const address = e.target[5].value; - const pk = await jsonToKey(keystore, password); - if (pk == null) { + const pk = await wallet.jsonToPrivateKey(keystore, password); + if (pk === null) { setErrorMessage('Password incorrect'); return; } const pkHex = Buffer.from(pk!.data()).toString('hex'); - const result = await findAddressWithPK(pkHex, address); - console.log(result); + const result = await wallet.findAddressWithPrivateKey(pkHex, address); setPk(pkHex); if (!result) { onOpen(); @@ -179,7 +179,7 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { - {errorMesssage != '' && ( + {errorMesssage !== '' && ( { diff --git a/src/ui/views/AddressImport/importComponent/KeyImport.tsx b/src/ui/views/AddressImport/importComponent/KeyImport.tsx index 35259955..05c8c341 100644 --- a/src/ui/views/AddressImport/importComponent/KeyImport.tsx +++ b/src/ui/views/AddressImport/importComponent/KeyImport.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithPK } from '../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../utils/modules/constants'; -import React from 'react'; import { Box, Button, Typography, TextField, TextareaAutosize } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; +import { KEY_TYPE } from '../../../utils/modules/constants'; + const useStyles = makeStyles((theme) => ({ form: { width: '100%', // Fix full width @@ -31,6 +32,7 @@ const useStyles = makeStyles((theme) => ({ const KeyImport = ({ onOpen, onImport, setPk, isSignLoading }) => { // const classes = useStyles(); const classes = useStyles(); + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); @@ -43,7 +45,7 @@ const KeyImport = ({ onOpen, onImport, setPk, isSignLoading }) => { const inputValue = e.target[2].value; setPk(pk); const address = flowAddressRegex.test(inputValue) ? inputValue : null; - const result = await findAddressWithPK(pk, address); + const result = await wallet.findAddressWithPrivateKey(pk, address); if (!result) { onOpen(); return; diff --git a/src/ui/views/AddressImport/importComponent/SeedPhrase.tsx b/src/ui/views/AddressImport/importComponent/SeedPhrase.tsx index dc6e4748..b7633456 100644 --- a/src/ui/views/AddressImport/importComponent/SeedPhrase.tsx +++ b/src/ui/views/AddressImport/importComponent/SeedPhrase.tsx @@ -1,11 +1,12 @@ -import { useEffect, useState, useContext } from 'react'; -import { findAddressWithSeed } from '../../../utils/modules/findAddressWithPK'; -import { KEY_TYPE } from '../../../utils/modules/constants'; -import React from 'react'; import { Box, Button, Typography, TextareaAutosize } from '@mui/material'; import { makeStyles } from '@mui/styles'; +import React, { useState } from 'react'; + +import { useWallet } from '@/ui/utils/WalletContext'; import { LLSpinner } from 'ui/FRWComponent'; + import KeyPathInput from '../../../FRWComponent/KeyPathInputs'; +import { KEY_TYPE } from '../../../utils/modules/constants'; const useStyles = makeStyles((theme) => ({ form: { @@ -32,6 +33,7 @@ const useStyles = makeStyles((theme) => ({ const SeedPhraseImport = ({ onOpen, onImport, setmnemonic, isSignLoading }) => { const classes = useStyles(); + const wallet = useWallet(); const [isLoading, setLoading] = useState(false); const handleImport = async (e) => { @@ -45,8 +47,7 @@ const SeedPhraseImport = ({ onOpen, onImport, setmnemonic, isSignLoading }) => { const address = flowAddressRegex.test(inputValue) ? inputValue : null; - console.log('address ', address); - const result = await findAddressWithSeed(seed, address, true); + const result = await wallet.findAddressWithSeedPhrase(seed, address, true); if (!result) { onOpen(); return; From 9b3d7f51d06bd716b1488c55967b252b09e1ad62 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:18:33 +1100 Subject: [PATCH 5/7] Removed direct background service imports Closes #298 --- src/background/controller/provider/controller.ts | 4 +--- src/background/controller/wallet.ts | 10 ++++++++++ src/background/utils/index.ts | 2 +- src/ui/views/Approval/components/Connect.tsx | 3 +-- src/ui/views/EvmMove/MoveFromChild/index.tsx | 3 +-- src/ui/views/EvmMove/MoveFromEvm/index.tsx | 3 +-- src/ui/views/EvmMove/MoveFromFlow/index.tsx | 3 +-- src/ui/views/EvmMove/MoveFromParent/index.tsx | 3 +-- 8 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/background/controller/provider/controller.ts b/src/background/controller/provider/controller.ts index 4e90bded..ff07a350 100644 --- a/src/background/controller/provider/controller.ts +++ b/src/background/controller/provider/controller.ts @@ -8,6 +8,7 @@ import RLP from 'rlp'; import Web3 from 'web3'; import { stringToHex } from 'web3-utils'; +import { signWithKey } from '@/background/utils/modules/publicPrivateKey'; import { ensureEvmAddressPrefix, isValidEthereumAddress } from '@/shared/utils/address'; import { permissionService, @@ -22,9 +23,6 @@ import { storage } from '../../webapi'; import BaseController from '../base'; import Wallet from '../wallet'; -// eslint-disable-next-line import/order -import { signWithKey } from '@/background/utils/modules/publicPrivateKey'; - interface Web3WalletPermission { // The name of the method corresponding to the permission parentCapability: string; diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index 31e5733f..d068d99b 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -386,6 +386,16 @@ export class WalletController extends BaseController { const { origin } = sessionService.getSession(tabId) || {}; return permissionService.getWithoutUpdate(origin); }; + addConnectedSite = ( + origin: string, + name: string, + icon: string, + defaultChain = 747, + isSigned = false + ) => { + permissionService.addConnectedSite(origin, name, icon, defaultChain, isSigned); + }; + updateConnectSite = (origin: string, data: ConnectedSite) => { permissionService.updateConnectSite(origin, data); // sessionService.broadcastEvent( diff --git a/src/background/utils/index.ts b/src/background/utils/index.ts index 85f6ac36..f3026eb9 100644 --- a/src/background/utils/index.ts +++ b/src/background/utils/index.ts @@ -4,7 +4,7 @@ import packageJson from '@/../package.json'; import { storage } from '@/background/webapi'; const { version } = packageJson; -import { mixpanelTrack } from '../service'; +import { mixpanelTrack } from '../service/mixpanel'; import pageStateCache from '../service/pageStateCache'; export { default as createPersistStore } from './persisitStore'; diff --git a/src/ui/views/Approval/components/Connect.tsx b/src/ui/views/Approval/components/Connect.tsx index c8baa075..7aee7b3a 100644 --- a/src/ui/views/Approval/components/Connect.tsx +++ b/src/ui/views/Approval/components/Connect.tsx @@ -4,7 +4,6 @@ import React, { useCallback, useEffect, useState } from 'react'; import { storage } from '@/background/webapi'; import { authnServiceDefinition, serviceDefinition } from 'background/controller/serviceDefinition'; -import { permissionService } from 'background/service'; import flowgrey from 'ui/FRWAssets/svg/flow-grey.svg'; import Link from 'ui/FRWAssets/svg/link.svg'; import linkGlobe from 'ui/FRWAssets/svg/linkGlobe.svg'; @@ -97,7 +96,7 @@ const Connect = ({ params: { /*icon, origin,*/ tabId } }: ConnectProps) => { chainId = 747; } console.log('permission add ', host, title, logo, chainId); - permissionService.addConnectedSite(host, title, logo, chainId); + wallet.addConnectedSite(host, title, logo, chainId); if (appIdentifier && nonce) { const message = WalletUtils.encodeAccountProof({ diff --git a/src/ui/views/EvmMove/MoveFromChild/index.tsx b/src/ui/views/EvmMove/MoveFromChild/index.tsx index d65104b6..f1c00d37 100644 --- a/src/ui/views/EvmMove/MoveFromChild/index.tsx +++ b/src/ui/views/EvmMove/MoveFromChild/index.tsx @@ -3,7 +3,6 @@ import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import wallet from '@/background/controller/wallet'; import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -159,7 +158,7 @@ const MoveFromChild = (props: TransferConfirmationProps) => { const moveToken = useCallback(async () => { setLoading(true); - const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); + const tokenResult = await usewallet.openapi.getTokenInfo(currentCoin, network); usewallet .moveFTfromChild(childUserInfo!.address, 'flowTokenProvider', amount!, tokenResult!.name) .then(async (createRes) => { diff --git a/src/ui/views/EvmMove/MoveFromEvm/index.tsx b/src/ui/views/EvmMove/MoveFromEvm/index.tsx index af1efe65..0539dfcc 100644 --- a/src/ui/views/EvmMove/MoveFromEvm/index.tsx +++ b/src/ui/views/EvmMove/MoveFromEvm/index.tsx @@ -3,7 +3,6 @@ import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import wallet from '@/background/controller/wallet'; import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -164,7 +163,7 @@ const MoveFromEvm = (props: TransferConfirmationProps) => { const bridgeToken = async () => { setLoading(true); - const tokenResult = await wallet.openapi.getEvmTokenInfo(currentCoin, network); + const tokenResult = await usewallet.openapi.getEvmTokenInfo(currentCoin, network); let flowId = tokenResult!['flowIdentifier']; diff --git a/src/ui/views/EvmMove/MoveFromFlow/index.tsx b/src/ui/views/EvmMove/MoveFromFlow/index.tsx index 219aff5a..d80d93c2 100644 --- a/src/ui/views/EvmMove/MoveFromFlow/index.tsx +++ b/src/ui/views/EvmMove/MoveFromFlow/index.tsx @@ -3,7 +3,6 @@ import { Box, Button, Typography, Drawer, IconButton, Grid } from '@mui/material import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import wallet from '@/background/controller/wallet'; import { withPrefix, isValidEthereumAddress } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -177,7 +176,7 @@ const MoveFromFlow = (props: TransferConfirmationProps) => { const bridgeToken = async () => { setLoading(true); - const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); + const tokenResult = await usewallet.openapi.getTokenInfo(currentCoin, network); console.log('tokenResult ', tokenResult); const address = tokenResult!.address.startsWith('0x') ? tokenResult!.address.slice(2) diff --git a/src/ui/views/EvmMove/MoveFromParent/index.tsx b/src/ui/views/EvmMove/MoveFromParent/index.tsx index bd56fd99..5e6f3a8d 100644 --- a/src/ui/views/EvmMove/MoveFromParent/index.tsx +++ b/src/ui/views/EvmMove/MoveFromParent/index.tsx @@ -4,7 +4,6 @@ import { ThemeProvider } from '@mui/material/styles'; import React, { useState, useEffect, useCallback } from 'react'; import { useHistory } from 'react-router-dom'; -import wallet from '@/background/controller/wallet'; import { isValidEthereumAddress, withPrefix } from '@/shared/utils/address'; import { WarningStorageLowSnackbar } from '@/ui/FRWComponent/WarningStorageLowSnackbar'; import { useStorageCheck } from '@/ui/utils/useStorageCheck'; @@ -160,7 +159,7 @@ const MoveFromParent = (props: TransferConfirmationProps) => { const moveToken = async () => { setLoading(true); - const tokenResult = await wallet.openapi.getTokenInfo(currentCoin, network); + const tokenResult = await usewallet.openapi.getTokenInfo(currentCoin, network); console.log('tokenResult ', tokenResult); usewallet .moveFTfromChild(childUserInfo!.address, 'flowTokenProvider', amount!, tokenResult!.name) From 7186bd156c6a8a021c8b888aa7b24cee8d9c5e15 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:53:34 +1100 Subject: [PATCH 6/7] jsonToPrivateKey now returns Hex as we can't pass a class object from the background to the UI Closes #298 --- src/background/controller/wallet.ts | 13 +++++++------ .../AddressImport/importComponent/JsonImport.tsx | 5 ++--- .../AddressImport/importComponent/JsonImport.tsx | 5 ++--- 3 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/background/controller/wallet.ts b/src/background/controller/wallet.ts index d068d99b..dd64e1b6 100644 --- a/src/background/controller/wallet.ts +++ b/src/background/controller/wallet.ts @@ -528,14 +528,15 @@ export class WalletController extends BaseController { return this._setCurrentAccountFromKeyring(keyring); }; - jsonToPrivateKey = (json: string, password: string) => { - return jsonToKey(json, password); + jsonToPrivateKeyHex = async (json: string, password: string): Promise => { + const pk = await jsonToKey(json, password); + return pk ? Buffer.from(pk.data()).toString('hex') : null; }; - findAddressWithPrivateKey = (pk: string, address: string) => { - return findAddressWithPK(pk, address); + findAddressWithPrivateKey = async (pk: string, address: string) => { + return await findAddressWithPK(pk, address); }; - findAddressWithSeedPhrase = (seed: string, address: string, isTemp: boolean = false) => { - return findAddressWithSeed(seed, address, isTemp); + findAddressWithSeedPhrase = async (seed: string, address: string, isTemp: boolean = false) => { + return await findAddressWithSeed(seed, address, isTemp); }; getPreMnemonics = () => keyringService.getPreMnemonics(); generatePreMnemonic = () => keyringService.generatePreMnemonic(); diff --git a/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx b/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx index 181b32f8..7176c50c 100644 --- a/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx +++ b/src/ui/views/AddWelcome/AddressImport/importComponent/JsonImport.tsx @@ -92,12 +92,11 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { } const password = e.target[2].value; const address = e.target[5].value; - const pk = await wallet.jsonToPrivateKey(keystore, password); - if (pk === null) { + const pkHex = await wallet.jsonToPrivateKeyHex(keystore, password); + if (pkHex === null) { setErrorMessage('Password incorrect'); return; } - const pkHex = Buffer.from(pk.data()).toString('hex'); const result = await wallet.findAddressWithPrivateKey(pkHex, address); setPk(pkHex); if (!result) { diff --git a/src/ui/views/AddressImport/importComponent/JsonImport.tsx b/src/ui/views/AddressImport/importComponent/JsonImport.tsx index fb161277..ac927411 100644 --- a/src/ui/views/AddressImport/importComponent/JsonImport.tsx +++ b/src/ui/views/AddressImport/importComponent/JsonImport.tsx @@ -92,12 +92,11 @@ const JsonImport = ({ onOpen, onImport, setPk, isSignLoading }) => { } const password = e.target[2].value; const address = e.target[5].value; - const pk = await wallet.jsonToPrivateKey(keystore, password); - if (pk === null) { + const pkHex = await wallet.jsonToPrivateKeyHex(keystore, password); + if (pkHex === null) { setErrorMessage('Password incorrect'); return; } - const pkHex = Buffer.from(pk!.data()).toString('hex'); const result = await wallet.findAddressWithPrivateKey(pkHex, address); setPk(pkHex); if (!result) { From 241e0901c4cca1eec5f348ead050ad24511f9676 Mon Sep 17 00:00:00 2001 From: Tom Beckenham <34339192+tombeckenham@users.noreply.github.com> Date: Wed, 18 Dec 2024 16:59:49 +1100 Subject: [PATCH 7/7] Minor upgrade of trustconnect library Closes #298 --- package.json | 2 +- pnpm-lock.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 86670118..e9813614 100644 --- a/package.json +++ b/package.json @@ -57,7 +57,7 @@ "@sentry/browser": "^6.19.7", "@sentry/react": "^6.19.7", "@sentry/tracing": "^6.19.7", - "@trustwallet/wallet-core": "^4.1.19", + "@trustwallet/wallet-core": "^4.1.21", "@tsparticles/engine": "^3.6.0", "@tsparticles/react": "^3.0.0", "@walletconnect/core": "^2.17.2", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index abac024c..82f2e8c2 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -96,7 +96,7 @@ importers: specifier: ^6.19.7 version: 6.19.7 '@trustwallet/wallet-core': - specifier: ^4.1.19 + specifier: ^4.1.21 version: 4.1.21 '@tsparticles/engine': specifier: ^3.6.0