import * as bip39 from 'bip39' ; import { EthereumAddress , HDKey } from 'wallet.ts' ; import { BigNumber } from 'bignumber.js' ; import { ec as EC } from 'elliptic' ; import { first } from 'lodash-unified' ; import { createTransaction } from '../../../../background/database/utils/openDB' ; import { createWalletDBAccess } from '../database/Wallet.db' ; import type { LegacyWalletRecord } from '../database/types' ; import { fromHex , toHex } from '@masknet/shared-base' ; import { currySameAddress , isSameAddress , ProviderType } from '@masknet/web3-shared-evm' ; import { LegacyWalletRecordOutDB } from './helpers' ; import { currentAccountSettings , currentProviderSettings } from '../settings' ; import { HD_PATH_WITHOUT_INDEX_ETHEREUM } from '../constants' ; import { hasNativeAPI } from '../../../utils/native-rpc' ; import { getAccounts } from '../../../extension/background-script/EthereumService' ; function sortWallet ( a , b ) { const address = currentAccountSettings . value ; if ( a . address === address ) return - 1 ; if ( b . address === address ) return 1 ; if ( a . updatedAt > b . updatedAt ) return - 1 ; if ( a . updatedAt < b . updatedAt ) return 1 ; if ( a . createdAt > b . createdAt ) return - 1 ; if ( a . createdAt < b . createdAt ) return 1 ; return 0 ; } function createWalletRecord ( address , name ) { const now = new Date ( ) ; return { address , name , erc20_token_whitelist : new Set ( ) , erc20_token_blacklist : new Set ( ) , erc721_token_whitelist : new Set ( ) , erc721_token_blacklist : new Set ( ) , erc1155_token_whitelist : new Set ( ) , erc1155_token_blacklist : new Set ( ) , mnemonic : [ ] , passphrase : '' , createdAt : now , updatedAt : now , } ; } export async function gatLegacyWallet ( address = currentAccountSettings . value ) { const wallets = await getLegacyWallets ( ) ; return wallets . find ( currySameAddress ( address ) ) ; } export async function getLegacyWallets ( provider ? ) { if ( hasNativeAPI ) { const accounts = await getAccounts ( ) ; const address = first ( accounts ) ?? '' ; if ( ! address ) return [ ] ; return [ createWalletRecord ( address , 'Mask Network' ) ] ; } const wallets = await getAllWalletRecords ( ) ; if ( provider === ProviderType . MaskWallet ) return wallets . filter ( ( x ) => x . _private_key_ || x . mnemonic . length ) ; if ( provider === currentProviderSettings . value ) return wallets . filter ( currySameAddress ( currentAccountSettings . value ) ) ; if ( provider ) return [ ] ; return wallets ; } export async function getLegacyWalletRecords ( ) { const wallets = await getAllWalletRecords ( ) ; return wallets . filter ( ( x ) => x . _private_key_ || x . mnemonic . length ) ; } async function getAllWalletRecords ( ) { const t = createTransaction ( await createWalletDBAccess ( ) , 'readonly' ) ( 'Wallet' ) ; const records = await t . objectStore ( 'Wallet' ) . getAll ( ) ; const wallets = ( await Promise . all < LegacyWalletRecord > ( records . map ( async ( record ) => { const walletRecord = LegacyWalletRecordOutDB ( record ) ; return { ... walletRecord , _private_key_ : await makePrivateKey ( walletRecord ) , } ; } ) ) ) . sort ( sortWallet ) ; return wallets ; } async function makePrivateKey ( record ) { if ( ! record . _private_key_ && ! record . mnemonic . length ) return '' ; const { privateKey } = record . _private_key_ ? await recoverWalletFromPrivateKey ( record . _private_key_ ) : await recoverWalletFromMnemonicWords ( record . mnemonic , record . passphrase , record . path ) ; return `0x${ toHex ( privateKey ) }` ; } export async function freezeLegacyWallet ( address ) { const walletStore = createTransaction ( await createWalletDBAccess ( ) , 'readwrite' ) ( 'Wallet' ) . objectStore ( 'Wallet' ) ; for await ( const cursor of walletStore ) { const wallet = cursor . value ; if ( isSameAddress ( wallet . address , address ) ) { await cursor . update ( { ... wallet , updatedAt : new Date ( 9999 , 1 , 1 ) , } ) ; break ; } } } async function recoverWalletFromMnemonicWords ( mnemonic , passphrase , path = `${ HD_PATH_WITHOUT_INDEX_ETHEREUM }/0` ) { const seed = await bip39 . mnemonicToSeed ( mnemonic . join ( ' ' ) , passphrase ) ; const masterKey = HDKey . parseMasterSeed ( seed ) ; const extendedPrivateKey = masterKey . derive ( path ) . extendedPrivateKey ! ; const childKey = HDKey . parseExtendedKey ( extendedPrivateKey ) ; const wallet = childKey . derive ( '' ) ; const walletPublicKey = wallet . publicKey ; const walletPrivateKey = wallet . privateKey ! ; return { address : EthereumAddress . from ( walletPublicKey ) . address , privateKey : walletPrivateKey , privateKeyValid : true , privateKeyInHex : `0x${ toHex ( walletPrivateKey ) }` , path , mnemonic , passphrase , } ; } async function recoverWalletFromPrivateKey ( privateKey ) { const ec = new EC ( 'secp256k1' ) ; const privateKey_ = privateKey . replace ( /^0x/ , '' ) . trim ( ) ; const key = ec . keyFromPrivate ( privateKey_ ) ; return { address : EthereumAddress . from ( key . getPublic ( false , 'array' ) as any ) . address , privateKey : fromHex ( privateKey_ ) , privateKeyValid : privateKeyVerify ( privateKey_ ) , privateKeyInHex : privateKey . startsWith ( '0x' ) ? privateKey : `0x${ privateKey }` , mnemonic : [ ] , } ; function privateKeyVerify ( key ) { if ( ! /[\da-f]{64}/i . test ( key ) ) return false ; const k = new BigNumber ( key , 16 ) ; const n = new BigNumber ( 'fffffffffffffffffffffffffffffffebaaedce6af48a03bbfd25e8cd0364141' , 16 ) ; return ! k . isZero ( ) && k . isLessThan ( n ) ; } }