Skip to content

Commit

Permalink
Merge pull request #2679 from near/zero-balance-ledger
Browse files Browse the repository at this point in the history
feat: Enable import of zero balance Ledger account (testnet)
  • Loading branch information
Patrick1904 authored May 17, 2022
2 parents d740b43 + 2033667 commit b8b8577
Show file tree
Hide file tree
Showing 4 changed files with 141 additions and 61 deletions.
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,39 +3,90 @@ 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);
} 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);
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.');
await setKeyMeta(ledgerPublicKey, { type: 'ledger' });
await setLedgerHdPath({ accountId: implicitAccountId, path: ledgerHdPath });
await wallet.importZeroBalanceAccount(implicitAccountId);
} else {
throw e;
}
}
} catch (e) {
dispatch(showCustomAlert({
errorMessage: e.message,
success: false,
messageCodeHeader: 'error'
}));
}
};

return (
<CouldNotFindAccountModal
isOpen={isOpen}
onClose={onClose}
recoveryMethod={recoveryMethod}
onClickImport={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
}));
if (recoveryMethod === 'ledger') {
await handleImportLedgerImplicitAccount();
} else {
await handleImportNonLedgerImplicitAccount();
}
dispatch(refreshAccount());
dispatch(redirectTo('/'));
dispatch(clearGlobalAlert());
}}
/>
);
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 @@ -40,6 +42,7 @@ export function SignInLedgerWrapper(props) {
const [accountId, setAccountId] = useState('');
const [loader, setLoader] = useState(false);
const [confirmedPath, setConfirmedPath] = useState(1);
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

0 comments on commit b8b8577

Please sign in to comment.