Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enable import of zero balance Ledger account (testnet) #2679

Merged
merged 3 commits into from
May 17, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,8 @@ const Container = styled.div`
export function CouldNotFindAccountModal ({
isOpen,
onClickImport,
onClose
onClose,
recoveryMethod
}) {
return (
<Modal
Expand All @@ -38,7 +39,10 @@ export function CouldNotFindAccountModal ({
>
<Container>
<h3><Translate id='recoverSeedPhrase.couldNotFindAccountModal.title'/></h3>
<p><Translate id='recoverSeedPhrase.couldNotFindAccountModal.desc'/></p>
{recoveryMethod === 'ledger'
? <p><Translate id='recoverSeedPhrase.couldNotFindAccountModal.desc.ledger'/></p>
: <p><Translate id='recoverSeedPhrase.couldNotFindAccountModal.desc.phrase'/></p>
}
<FormButton onClick={onClickImport}><Translate id='recoverSeedPhrase.couldNotFindAccountModal.buttonImport'/></FormButton>
<FormButton className='link' onClick={onClose}><Translate id='button.cancel'/></FormButton>
</Container>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,38 +3,91 @@ import { useDispatch } from 'react-redux';

import {
redirectTo,
refreshAccount
refreshAccount,
getLedgerPublicKey
} from '../../redux/actions/account';
import { showCustomAlert, clearGlobalAlert } from '../../redux/actions/status';
import { setLedgerHdPath } from '../../utils/localStorage';
import { getImplicitAccountIdFromSeedPhrase, getKeyPairFromSeedPhrase } from '../../utils/parseSeedPhrase';
import { wallet } from '../../utils/wallet';
import { wallet, setKeyMeta } from '../../utils/wallet';
import { CouldNotFindAccountModal } from './CouldNotFindAccountModal';

export function CouldNotFindAccountModalWrapper ({
export function CouldNotFindAccountModalWrapper({
isOpen,
onClose,
seedPhrase
seedPhrase,
ledgerHdPath,
recoveryMethod
}) {
const dispatch = useDispatch();

const handleImportNonLedgerImplicitAccount = async () => {
const recoveryKeyPair = getKeyPairFromSeedPhrase(seedPhrase);
const implicitAccountId = getImplicitAccountIdFromSeedPhrase(seedPhrase);
try {
await wallet.importZeroBalanceAccount(implicitAccountId, recoveryKeyPair);
dispatch(refreshAccount());
dispatch(redirectTo('/'));
dispatch(clearGlobalAlert());
} catch (e) {
dispatch(showCustomAlert({
success: false,
messageCodeHeader: 'error',
messageCode: 'walletErrorCodes.recoverAccountSeedPhrase.errorNotAbleToImportAccount',
errorMessage: e.message
}));
}
};

const handleImportLedgerImplicitAccount = async () => {
try {
const ledgerPublicKey = await dispatch(getLedgerPublicKey(ledgerHdPath));
const implicitAccountId = Buffer.from(ledgerPublicKey.data).toString('hex');
const account = wallet.getAccountBasic(implicitAccountId);
try {
const accountState = await account.state();
if (accountState) {
const errorMessage = `Implicit account ID ${implicitAccountId} derived from Ledger HD path ${ledgerHdPath} already exists but is not controlled by this Ledger device.`;
// This could happen if user creates an implicit account with Ledger but then switches to seed phrase recovery, etc.
console.log(errorMessage);
Patrick1904 marked this conversation as resolved.
Show resolved Hide resolved
dispatch(showCustomAlert({
errorMessage: errorMessage,
success: false,
messageCodeHeader: 'error'
}));
return;
}
} catch (e) {
if (e.message.includes('does not exist while viewing')) {
console.log('Ledger implicit Account ID does not exist on chain. Importing as zero balance account.');
Patrick1904 marked this conversation as resolved.
Show resolved Hide resolved
await setKeyMeta(ledgerPublicKey, { type: 'ledger' });
await setLedgerHdPath({ accountId: implicitAccountId, path: ledgerHdPath });
await wallet.importZeroBalanceAccount(implicitAccountId);
dispatch(refreshAccount());
dispatch(redirectTo('/'));
} else {
throw e;
}
}
} catch (e) {
dispatch(showCustomAlert({
errorMessage: e.message,
success: false,
messageCodeHeader: 'error'
}));
}
};

return (
<CouldNotFindAccountModal
isOpen={isOpen}
onClose={onClose}
recoveryMethod={recoveryMethod}
onClickImport={async () => {
Patrick1904 marked this conversation as resolved.
Show resolved Hide resolved
const recoveryKeyPair = getKeyPairFromSeedPhrase(seedPhrase);
const implicitAccountId = getImplicitAccountIdFromSeedPhrase(seedPhrase);
try {
await wallet.importZeroBalanceAccount(implicitAccountId, recoveryKeyPair);
dispatch(refreshAccount());
dispatch(redirectTo('/'));
dispatch(clearGlobalAlert());
} catch (e) {
dispatch(showCustomAlert({
success: false,
messageCodeHeader: 'error',
messageCode: 'walletErrorCodes.recoverAccountSeedPhrase.errorNotAbleToImportAccount',
errorMessage: e.message
}));
if (recoveryMethod === 'ledger') {
await handleImportLedgerImplicitAccount();
} else {
await handleImportNonLedgerImplicitAccount();
}
}}
/>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { parse as parseQuery, stringify } from 'query-string';
import React, { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';

import { IMPORT_ZERO_BALANCE_ACCOUNT } from '../../../../../../features';
import { CouldNotFindAccountModalWrapper } from '../../../components/accounts/CouldNotFindAccountModalWrapper';
import { Mixpanel } from '../../../mixpanel/index';
import {
redirectToApp,
Expand All @@ -20,7 +22,7 @@ import EnterAccountId from './SignInLedgerViews/EnterAccountId';
import ImportAccounts from './SignInLedgerViews/ImportAccounts';
import SignIn from './SignInLedgerViews/SignIn';

const {
const {
signInWithLedger,
signInWithLedgerAddAndSaveAccounts,
clearSignInWithLedgerModalState
Expand All @@ -39,7 +41,8 @@ export function SignInLedgerWrapper(props) {

const [accountId, setAccountId] = useState('');
const [loader, setLoader] = useState(false);
const [confirmedPath, setConfirmedPath] = useState(1);
const [confirmedPath, setConfirmedPath] = useState(10);
Patrick1904 marked this conversation as resolved.
Show resolved Hide resolved
const [showCouldNotFindAccountModal, setShowCouldNotFindAccountModal] = useState(false);
const ledgerHdPath = `${LEDGER_HD_PATH_PREFIX}${confirmedPath}'`;

const account = useSelector(selectAccountSlice);
Expand All @@ -64,6 +67,14 @@ export function SignInLedgerWrapper(props) {
dispatch(clearSignInWithLedgerModalState());
}, []);

useEffect(() => {
if (IMPORT_ZERO_BALANCE_ACCOUNT) {
if (signInWithLedgerStatus === LEDGER_MODAL_STATUS.ENTER_ACCOUNTID) {
setShowCouldNotFindAccountModal(true);
}
}
}, [signInWithLedgerStatus]);

const handleChange = (value) => {
setAccountId(value);
};
Expand Down Expand Up @@ -115,50 +126,61 @@ export function SignInLedgerWrapper(props) {
const handleCancelSignIn = () => {
dispatch(clearSignInWithLedgerModalState());
};

const handleCancelAuthorize = () => {
dispatch(redirectTo('/recover-account'));
};

return (
<Container className='small-centered border ledger-theme'>
{!signInWithLedgerStatus &&
<Authorize
confirmedPath={confirmedPath}
setConfirmedPath={setConfirmedPath}
handleSignIn={handleSignIn}
signingIn={!!signInWithLedgerStatus}
handleCancel={handleCancelAuthorize}
/>
}
{signInWithLedgerStatus === LEDGER_MODAL_STATUS.CONFIRM_PUBLIC_KEY &&
<SignIn
txSigned={txSigned}
handleCancel={handleCancelSignIn}
/>
}
{signInWithLedgerStatus === LEDGER_MODAL_STATUS.ENTER_ACCOUNTID &&
<EnterAccountId
handleAdditionalAccountId={handleAdditionalAccountId}
handleChange={handleChange}
checkAccountAvailable={(accountId) => dispatch(checkAccountAvailable(accountId))}
mainLoader={mainLoader}
stateAccountId={account.accountId}
loader={loader}
clearSignInWithLedgerModalState={() => dispatch(clearSignInWithLedgerModalState())}
<>
<Container className='small-centered border ledger-theme'>
{!signInWithLedgerStatus &&
<Authorize
confirmedPath={confirmedPath}
setConfirmedPath={setConfirmedPath}
handleSignIn={handleSignIn}
signingIn={!!signInWithLedgerStatus}
handleCancel={handleCancelAuthorize}
/>
}
{signInWithLedgerStatus === LEDGER_MODAL_STATUS.CONFIRM_PUBLIC_KEY &&
<SignIn
txSigned={txSigned}
handleCancel={handleCancelSignIn}
/>
}
{signInWithLedgerStatus === LEDGER_MODAL_STATUS.ENTER_ACCOUNTID &&
<EnterAccountId
handleAdditionalAccountId={handleAdditionalAccountId}
handleChange={handleChange}
checkAccountAvailable={(accountId) => dispatch(checkAccountAvailable(accountId))}
mainLoader={mainLoader}
stateAccountId={account.accountId}
loader={loader}
clearSignInWithLedgerModalState={() => dispatch(clearSignInWithLedgerModalState())}
/>
}
{(signInWithLedgerStatus === LEDGER_MODAL_STATUS.CONFIRM_ACCOUNTS || signInWithLedgerStatus === LEDGER_MODAL_STATUS.SUCCESS) &&
<ImportAccounts
accountsApproved={accountsApproved}
totalAccounts={totalAccounts}
ledgerAccounts={ledgerAccounts}
accountsError={accountsError}
accountsRejected={accountsRejected}
signInWithLedgerStatus={signInWithLedgerStatus}
handleContinue={handleContinue}
/>
}
</Container>
{showCouldNotFindAccountModal && (
<CouldNotFindAccountModalWrapper
onClose={() => setShowCouldNotFindAccountModal(false)}
isOpen={showCouldNotFindAccountModal}
recoveryMethod='ledger'
ledgerHdPath={ledgerHdPath}
// TODO: Provide ledger public key as prop to avoid asking for it again
/>
}
{(signInWithLedgerStatus === LEDGER_MODAL_STATUS.CONFIRM_ACCOUNTS || signInWithLedgerStatus === LEDGER_MODAL_STATUS.SUCCESS) &&
<ImportAccounts
accountsApproved={accountsApproved}
totalAccounts={totalAccounts}
ledgerAccounts={ledgerAccounts}
accountsError={accountsError}
accountsRejected={accountsRejected}
signInWithLedgerStatus={signInWithLedgerStatus}
handleContinue={handleContinue}
/>
}
</Container>
)}
</>
);
}
5 changes: 4 additions & 1 deletion packages/frontend/src/translations/en.global.json
Original file line number Diff line number Diff line change
Expand Up @@ -896,7 +896,10 @@
"recoverSeedPhrase": {
"couldNotFindAccountModal": {
"buttonImport": "Import Anyway",
"desc": "We couldn't find any <b>active account(s)</b> using the provided passphrase. This could be because the passphrase is incorrect, or because the account has no activity yet.",
"desc": {
"ledger": "We couldn't find any <b>active account(s)</b> using the provided Ledger key. This could be because the account has no activity yet.",
"phrase": "We couldn't find any <b>active account(s)</b> using the provided passphrase. This could be because the passphrase is incorrect, or because the account has no activity yet."
},
"title": "Couldn't find account"
},
"pageText": "Enter the backup passphrase associated with the account.",
Expand Down