Skip to content
This repository has been archived by the owner on Nov 5, 2023. It is now read-only.

Commit

Permalink
Wallet recovery (#342)
Browse files Browse the repository at this point in the history
* Add addRecoveryWallet function to the keyring controller

* Get wallet hash from the recovery address so user no longer needs to copy that over

* Update types for addRecoveryWallet method

* Fixing the add recovery wallet function

* Use aggregator url from currently selected network

* Adding a param for the signers private key and saving the new wallet in the keyring

* added UI for entering recovery hash (#355)

* added UI for entering recovery hash

* added ui for salt

* completed workflow for wallet recovery

* lint: fix

* updated hex conversion of salt

* using temp wallet for recovery

* replace address after recovery

* Lint fix

* Adding randFr() for PK generation

* Add back button to recovery modal

* Missed some files to commit

* Fix address issues using randFr

* Fix randFr function by initing the mcl

* Fix lint issue

Co-authored-by: Kautuk Kundan <[email protected]>
  • Loading branch information
blakecduncan and kautukkundan authored Dec 15, 2022
1 parent 46dfe7b commit 9078f61
Show file tree
Hide file tree
Showing 11 changed files with 533 additions and 7 deletions.
1 change: 1 addition & 0 deletions extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@
"fp-ts": "^2.12.1",
"io-ts": "^2.2.16",
"lodash-es": "^4.17.21",
"mcl-wasm": "^1.0.3",
"phosphor-react": "^1.4.0",
"qrcode.react": "^3.1.0",
"react": "^17.0.2",
Expand Down
108 changes: 108 additions & 0 deletions extension/source/Home/Wallet/Wallets/Recovery/RecoverWalletModal.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { FunctionComponent, useState } from 'react';
import Modal from 'react-modal';
import Button from '../../../../components/Button';
import Range from '../../../../helpers/Range';
import StepOneInfo from './StepOneInfo';
import StepThreeRecover from './StepThreeRecover';
import StepTwoWalletCreation from './StepTwoWalletCreation';

const WorkflowNumbers: FunctionComponent<{
max: number;
current: number;
}> = ({ max, current }) => {
return (
<div className="flex justify-center space-x-10">
{Range(max).map((i) => (
<div
key={i}
className={[
'icon-lg',
'rounded-full',
'text-center',
'leading-8',
'cursor-pointer',
...(i <= current ? ['bg-blue-500', 'text-white'] : ['text-black']),
].join(' ')}
>
{i + 1}
</div>
))}
</div>
);
};

const RecoverWalletModal = () => {
const [modalIsOpen, setIsOpen] = useState<boolean>(false);
const [pageIndex, setPageIndex] = useState<number>(0);
const [walletPrivateKey, setWalletPrivateKey] = useState<string>('');
const [walletAddress, setWalletAddress] = useState<string>('');

const onRecoverComplete = () => {
setWalletPrivateKey('');
setWalletAddress('');
setIsOpen(false);
};

return (
<div>
<Button onPress={() => setIsOpen(true)} className="btn-secondary">
Import
</Button>
<Modal
isOpen={modalIsOpen}
onRequestClose={() => setIsOpen(false)}
style={{
content: {
width: '35rem',
margin: 'auto',
fontFamily: 'montserrat',
padding: 0,
border: 'none',
height: '25rem',
},
overlay: {
backgroundColor: 'rgba(0, 0, 0, 0.5)',
},
}}
>
<div className="p-8 h-full w-100 flex flex-col">
<WorkflowNumbers max={3} current={pageIndex} />
<div className="mt-8 h-100 flex-grow">
{
[
<StepOneInfo
key={1}
onComplete={() => {
setPageIndex(1);
}}
/>,
<StepTwoWalletCreation
key={2}
setWalletPkToParent={setWalletPrivateKey}
setWalletAddressToParent={setWalletAddress}
walletAddress={walletAddress}
onBack={() => {
setPageIndex(0);
}}
onComplete={() => {
setPageIndex(2);
}}
/>,
<StepThreeRecover
key={3}
walletPrivateKey={walletPrivateKey}
onBack={() => {
setPageIndex(1);
}}
onComplete={() => onRecoverComplete()}
/>,
][pageIndex]
}
</div>
</div>
</Modal>
</div>
);
};

export default RecoverWalletModal;
38 changes: 38 additions & 0 deletions extension/source/Home/Wallet/Wallets/Recovery/StepOneInfo.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import { CaretRight } from 'phosphor-react';
import { FunctionComponent } from 'react';
import Button from '../../../../components/Button';

const StepOneInfo: FunctionComponent<{
onComplete: () => void;
}> = ({ onComplete }) => {
return (
<div className="flex flex-col gap-4 h-full">
<div className="flex-grow">
<div className="text-[14pt]">Recover existing wallet in Quill</div>
<div className="text-[10pt] text-grey-700 leading-loose">
You can recover existing instant BLS wallets into Quill. This is a
simple 2 step process which requires you to copy-paste two values from
Quill to the instant wallet and then again from instant wallet to
Quill
</div>
<br />
<div className="text-[10pt] text-grey-700 leading-loose font-bold mt-2">
Do not close this modal until you have completed all the steps that
follow, else you will lose access to your original keys!
</div>
</div>

<div className="flex justify-end">
<Button
onPress={() => onComplete()}
className="btn-primary h-10 text-[10pt] w-1/3"
icon={<CaretRight size={15} />}
>
Continue
</Button>
</div>
</div>
);
};

export default StepOneInfo;
101 changes: 101 additions & 0 deletions extension/source/Home/Wallet/Wallets/Recovery/StepThreeRecover.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
import { ethers } from 'ethers';
import { Download, CaretLeft } from 'phosphor-react';
import { FunctionComponent, useState } from 'react';
import Button from '../../../../components/Button';
import { useQuill } from '../../../../QuillContext';

const StepThreeRecover: FunctionComponent<{
onBack: () => void;
onComplete: () => void;
walletPrivateKey: string;
}> = ({ onBack, onComplete, walletPrivateKey }) => {
const { rpc } = useQuill();

const [salt, setSalt] = useState<string>('');
const [instantWalletAddress, setInstantWalletAddress] = useState<string>('');

const handleRecover = async () => {
await rpc.addRecoveryWallet(
instantWalletAddress,
ethers.utils.formatBytes32String(salt),
walletPrivateKey,
);
onComplete();
};

return (
<div className="flex flex-col gap-4 h-full">
<div className="flex-grow">
<div className="text-[14pt]">Almost There</div>
<div className="text-[10pt] text-grey-700 leading-loose">
Your instant wallet recovery is in process. Once finished the same
wallet will be visible inside Quill.
<br />
<div
className={[
'text-[10pt]',
'text-grey-700',
'leading-loose',
'font-bold',
'mt-2',
].join(' ')}
>
Copy back the instant wallet address and salt entered
</div>
</div>

<div className="mt-4 flex flex-col gap-2">
<input
type="text"
className={[
'bg-opacity-5',
'border-opacity-45',
'focus:border-opacity-85',
'h-10',
'text-[10pt]',
].join(' ')}
placeholder="Instant Wallet Address"
onChange={(e) => {
setInstantWalletAddress(e.target.value);
}}
/>

<input
type="text"
className={[
'bg-opacity-5',
'border-opacity-45',
'focus:border-opacity-85',
'h-10',
'text-[10pt]',
].join(' ')}
placeholder="Recovery Salt"
onChange={(e) => {
setSalt(e.target.value);
}}
/>
</div>
</div>

<div className="flex justify-between">
<Button
onPress={() => onBack()}
className="btn-primary h-10 text-[10pt] w-1/3"
iconLeft={<CaretLeft size={15} />}
>
Back
</Button>

<Button
onPress={() => handleRecover()}
className="btn-primary h-10 text-[10pt] w-1/3"
icon={<Download size={15} />}
>
Recover Wallet
</Button>
</div>
</div>
);
};

export default StepThreeRecover;
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
import { CaretRight, CaretLeft, CircleNotch, CopySimple } from 'phosphor-react';
import { FunctionComponent, useEffect, useState } from 'react';
import Button from '../../../../components/Button';
import { useQuill } from '../../../../QuillContext';

const StepTwoWalletCreation: FunctionComponent<{
onBack: () => void;
onComplete: () => void;
setWalletPkToParent: (address: string) => void;
setWalletAddressToParent: (address: string) => void;
walletAddress: string;
}> = ({
onBack,
onComplete,
setWalletPkToParent,
walletAddress,
setWalletAddressToParent,
}) => {
const [loading, setLoading] = useState<boolean>(!walletAddress);

const { rpc } = useQuill();

useEffect(() => {
const createWallet = async () => {
setLoading(true);
const { address, privateKey } = await rpc.createTempAccount();
setWalletAddressToParent(address);
setWalletPkToParent(privateKey);
setLoading(false);
};

if (!walletAddress) {
createWallet();
}
}, [walletAddress, rpc, setWalletPkToParent, setWalletAddressToParent]);

return (
<div className="flex flex-col gap-4 h-full">
<div className="flex-grow">
<div className="text-[14pt]">Recovery Wallet</div>
<div className="text-[10pt] text-grey-700 leading-loose">
This is the wallet address which will be used to recover your instant
wallet.
</div>
<br />
<div className="text-[10pt] text-grey-700 leading-loose font-bold mt-2">
Copy the address and paste it in the instant wallet.
</div>
<div className="mt-4 bg-grey-900 bg-opacity-25 rounded-md p-5">
{loading ? (
<div className="flex gap-4 text-[12pt] items-center">
Generating wallet
<div className="animate-spin relative">
<CircleNotch size={30} />
</div>
</div>
) : (
<div className="flex justify-between">
<div className="font-mono text-[11pt]">{walletAddress}</div>
{/* eslint-disable-next-line */}
<div
className="cursor-pointer"
onClick={() => navigator.clipboard.writeText(walletAddress)}
>
<CopySimple size={20} />
</div>
</div>
)}
</div>
</div>

<div className="flex justify-between">
<Button
onPress={() => onBack()}
className="btn-primary h-10 text-[10pt] w-1/3"
iconLeft={<CaretLeft size={15} />}
>
Back
</Button>

<Button
onPress={() => onComplete()}
className="btn-primary h-10 text-[10pt] w-1/3"
icon={<CaretRight size={15} />}
>
Continue
</Button>
</div>
</div>
);
};

export default StepTwoWalletCreation;
10 changes: 7 additions & 3 deletions extension/source/Home/Wallet/Wallets/WalletWrapper.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import useCell from '../../../cells/useCell';
import Button from '../../../components/Button';
import Loading from '../../../components/Loading';
import { useQuill } from '../../../QuillContext';
import RecoverWalletModal from './Recovery/RecoverWalletModal';
/* eslint import/no-cycle: "warn" -- TODO (merge-ok) Fix import cycle */
import { WalletSummary } from './WalletSummary';

Expand All @@ -25,9 +26,12 @@ export const WalletsWrapper: FunctionComponent = () => {
<div className="">
<div className="flex justify-between place-items-center">
<div className="text-body">Wallets</div>
<Button onPress={rpc.addHDAccount} className="btn-secondary">
Add
</Button>
<div className="flex gap-2">
<RecoverWalletModal />
<Button onPress={rpc.addHDAccount} className="btn-primary">
Add
</Button>
</div>
</div>

{!ethAccounts && <Loading />}
Expand Down
Loading

0 comments on commit 9078f61

Please sign in to comment.