Skip to content

Commit

Permalink
15 word shelley wallet support (#1628)
Browse files Browse the repository at this point in the history
  • Loading branch information
SebastienGllmt committed Aug 3, 2020
1 parent d93d472 commit 73566ea
Show file tree
Hide file tree
Showing 25 changed files with 431 additions and 182 deletions.
30 changes: 23 additions & 7 deletions app/actions/common/wallet-restore-actions.js
@@ -1,6 +1,7 @@
// @flow

import { AsyncAction, Action } from '../lib/Action';
import config from '../../config';

export type WalletRestoreMeta = {|
recoveryPhrase: string,
Expand All @@ -9,13 +10,28 @@ export type WalletRestoreMeta = {|
paperPassword: string,
|};

export const RestoreMode = Object.freeze({
UNSET: -1,
REGULAR_15: 0,
REGULAR_24: 2,
PAPER: 1,
});
export type RestoreModeType = $Values<typeof RestoreMode>;
export type RestoreModeType = {|
type: 'bip44',
extra: void,
length: (
typeof config.wallets.WALLET_RECOVERY_PHRASE_WORD_COUNT |
typeof config.wallets.DAEDALUS_RECOVERY_PHRASE_WORD_COUNT
),
|} | {|
type: 'cip1852',
extra: void,
length: (
typeof config.wallets.WALLET_RECOVERY_PHRASE_WORD_COUNT |
typeof config.wallets.DAEDALUS_SHELLEY_RECOVERY_PHRASE_WORD_COUNT
),
|} | {|
type: 'bip44',
extra: 'paper',
length: (
typeof config.wallets.YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT |
typeof config.wallets.DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT
),
|};

export default class WalletRestoreActions {
submitFields: Action<WalletRestoreMeta> = new Action();
Expand Down
83 changes: 83 additions & 0 deletions app/components/wallet/add/option-dialog/WalletEraOptionDialog.js
@@ -0,0 +1,83 @@
// @flow
import type { Node } from 'react';
import React, { Component } from 'react';
import { observer } from 'mobx-react';
import { defineMessages, intlShape } from 'react-intl';

import Dialog from '../../../widgets/Dialog';
import DialogCloseButton from '../../../widgets/DialogCloseButton';
import OptionBlock from '../../../widgets/options/OptionBlock';
import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import styles from '../../../widgets/options/OptionListWrapperStyle.scss';
import DialogBackButton from '../../../widgets/DialogBackButton';

const messages = defineMessages({
dialogTitle: {
id: 'wallet.add.optionDialog.walletEra.dialogTitle',
defaultMessage: '!!!Choose era',
},
restoreByronEraWalletTitle: {
id: 'wallet.add.optionDialog.walletEra.byronEra.title',
defaultMessage: '!!!Byron-era (read-only) wallet',
},
restoreByronEraWalletDescription: {
id: 'wallet.add.optionDialog.walletEra.byronEra.description',
defaultMessage: '!!!Wallets created before July 29th, 2020 are Byron-era wallets and cannot delegate.',
},
restoreShelleyEraWalletTitle: {
id: 'wallet.add.optionDialog.walletEra.shelleyEra.title',
defaultMessage: '!!!Shelley-era wallet',
},
restoreShelleyEraWalletDescription: {
id: 'wallet.add.optionDialog.walletEra.shelleyEra.description',
defaultMessage: '!!!Shelley-era wallets support delegation to stake pools.',
},
});

type Props = {|
+onCancel: void => void,
+onByron: void => void,
+onShelley: void => void,
+onBack: void => void,
|};

@observer
export default class WalletEraOptionDialog extends Component<Props> {
static contextTypes: {|intl: $npm$ReactIntl$IntlFormat|} = {
intl: intlShape.isRequired,
};

render(): Node {
const { intl } = this.context;

return (
<Dialog
title={intl.formatMessage(messages.dialogTitle)}
closeOnOverlayClick={false}
onClose={this.props.onCancel}
closeButton={<DialogCloseButton />}
className="WalletEraOptionDialog"
backButton={<DialogBackButton onBack={this.props.onBack} />}
>
<div className={styles.component}>
<ul className={styles.optionBlockList}>
<OptionBlock
parentName="WalletEraOptionDialog"
type="bgByronMainnet"
title={intl.formatMessage(messages.restoreByronEraWalletTitle)}
learnMoreText={intl.formatMessage(messages.restoreByronEraWalletDescription)}
onSubmit={this.props.onByron}
/>
<OptionBlock
parentName="WalletEraOptionDialog"
type="bgShelleyMainnet"
title={intl.formatMessage(messages.restoreShelleyEraWalletTitle)}
learnMoreText={intl.formatMessage(messages.restoreShelleyEraWalletDescription)}
onSubmit={this.props.onShelley}
/>
</ul>
</div>
</Dialog>
);
}
}
Expand Up @@ -75,7 +75,7 @@ export default class WalletRestoreOptionDialog extends Component<Props> {
{onRestore24 != null && (
<OptionBlock
parentName="WalletRestoreOptionDialog"
type="restoreNormalWallet"
type="normal24WordWallet"
title={intl.formatMessage(messages.restoreNormal24Title)}
learnMoreText={intl.formatMessage(messages.restoreNormal24Description)}
onSubmit={onRestore24}
Expand Down
24 changes: 22 additions & 2 deletions app/components/widgets/options/OptionBlock.scss
Expand Up @@ -160,10 +160,20 @@
&.restoreNormalWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-normal-wallet-classic.inline.svg);
}

&.normal24WordWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-normal-wallet-classic.inline.svg);
}
&.restorePaperWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-paper-wallet-classic.inline.svg);
}
&.bgByronMainnet {
background-image: url(../../../assets/images/claim/byron-mainnet.inline.svg);
background-position: center 20px !important;
}
&.bgShelleyMainnet {
background-image: url(../../../assets/images/claim/shelley-mainnet.inline.svg);
background-position: center 20px !important;
}
}
}
}
Expand Down Expand Up @@ -211,10 +221,20 @@
&.restoreNormalWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-normal-wallet-modern.inline.svg);
}

&.normal24WordWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-normal-wallet-modern.inline.svg);
}
&.restorePaperWallet {
background-image: url(../../../assets/images/add-wallet/option-dialog/restore-paper-wallet-modern.inline.svg);
}
&.bgByronMainnet {
background-image: url(../../../assets/images/claim/byron-mainnet.inline.svg);
background-position: center 20px !important;
}
&.bgShelleyMainnet {
background-image: url(../../../assets/images/claim/shelley-mainnet.inline.svg);
background-position: center 20px !important;
}
}
}
}
Expand Down
34 changes: 17 additions & 17 deletions app/config.js
@@ -1,18 +1,18 @@
// @flow

export default {
wallets: {
ADDRESS_COPY_TOOLTIP_NOTIFICATION_DURATION: 2,
WALLET_CREATED_NOTIFICATION_DURATION: 8,
WALLET_RESTORED_NOTIFICATION_DURATION: 8,
MAX_ALLOWED_UNUSED_ADDRESSES: 20,
TRANSACTION_REQUEST_SIZE: 50,
DAEDALUS_RECOVERY_PHRASE_WORD_COUNT: 12,
DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT: 27,
MAX_RECOVERY_PHRASE_WORD_COUNT: 24,
WALLET_RECOVERY_PHRASE_WORD_COUNT: 15,
DAEDALUS_SHELLEY_RECOVERY_PHRASE_WORD_COUNT: 24,
YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT: 21,
export default Object.freeze({
wallets: Object.freeze({
ADDRESS_COPY_TOOLTIP_NOTIFICATION_DURATION: (2: 2),
WALLET_CREATED_NOTIFICATION_DURATION: (8: 8),
WALLET_RESTORED_NOTIFICATION_DURATION: (8: 8),
MAX_ALLOWED_UNUSED_ADDRESSES: (20: 20),
TRANSACTION_REQUEST_SIZE: (50: 50),
DAEDALUS_RECOVERY_PHRASE_WORD_COUNT: (12: 12),
DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT: (27: 27),
MAX_RECOVERY_PHRASE_WORD_COUNT: (24: 24),
WALLET_RECOVERY_PHRASE_WORD_COUNT: (15: 15),
DAEDALUS_SHELLEY_RECOVERY_PHRASE_WORD_COUNT: (24: 24),
YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT: (21: 21),
hardwareWallet: {
trezorT: {
VENDOR: 'trezor.io',
Expand All @@ -31,8 +31,8 @@ export default {
VENDOR: 'ledger.com',
}
}
},
forms: {
}),
forms: Object.freeze({
FORM_VALIDATION_DEBOUNCE_WAIT: 500
},
};
}),
});
11 changes: 4 additions & 7 deletions app/containers/transfer/DaedalusTransferPage.js
Expand Up @@ -20,7 +20,7 @@ import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import { SelectedExplorer } from '../../domain/SelectedExplorer';
import type { UnitOfAccountSettingType } from '../../types/unitOfAccountType';
import { PublicDeriver } from '../../api/ada/lib/storage/models/PublicDeriver/index';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';
import type { RestoreModeType } from '../../actions/common/wallet-restore-actions';
import { formattedWalletAmount } from '../../utils/formatters';
import { ROUTES } from '../../routes-config';
import { ApiOptions, getApiForNetwork, getApiMeta } from '../../api/common/utils';
Expand Down Expand Up @@ -140,8 +140,7 @@ export default class DaedalusTransferPage extends Component<InjectedOrGenerated<
onBack={this.backToUninitialized}
mnemonicValidator={mnemonic => this.generated.stores.walletRestore.isValidMnemonic({
mnemonic,
numberOfWords: config.wallets.DAEDALUS_RECOVERY_PHRASE_WORD_COUNT,
mode: RestoreMode.REGULAR_15,
mode: { type: 'bip44', extra: undefined, length: config.wallets.DAEDALUS_RECOVERY_PHRASE_WORD_COUNT },
})}
validWords={validWords}
mnemonicLength={config.wallets.DAEDALUS_RECOVERY_PHRASE_WORD_COUNT}
Expand All @@ -155,8 +154,7 @@ export default class DaedalusTransferPage extends Component<InjectedOrGenerated<
onBack={this.backToUninitialized}
mnemonicValidator={mnemonic => this.generated.stores.walletRestore.isValidMnemonic({
mnemonic,
numberOfWords: config.wallets.DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT,
mode: RestoreMode.PAPER,
mode: { type: 'bip44', extra: 'paper', length: config.wallets.DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT },
})}
validWords={validWords}
mnemonicLength={config.wallets.DAEDALUS_PAPER_RECOVERY_PHRASE_WORD_COUNT}
Expand Down Expand Up @@ -269,8 +267,7 @@ export default class DaedalusTransferPage extends Component<InjectedOrGenerated<
walletRestore: {|
isValidMnemonic: ({|
mnemonic: string,
numberOfWords: number,
mode: $PropertyType<typeof RestoreMode, 'REGULAR_15'> | $PropertyType<typeof RestoreMode, 'REGULAR_24'>| $PropertyType<typeof RestoreMode, 'PAPER'>,
mode: RestoreModeType,
|}) => boolean,
|},
daedalusTransfer: {|
Expand Down
12 changes: 4 additions & 8 deletions app/containers/transfer/DaedalusTransferPage.stories.js
Expand Up @@ -29,7 +29,6 @@ import {
NotEnoughMoneyToSendError,
} from '../../api/common/errors';
import AdaApi from '../../api/ada/index';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';

export default {
title: `${__filename.split('.')[0]}`,
Expand Down Expand Up @@ -58,14 +57,11 @@ const genBaseProps: {|
},
walletRestore: {
isValidMnemonic: (isValidRequest) => {
const { mnemonic, numberOfWords } = isValidRequest;
if (
(isValidRequest.mode === RestoreMode.REGULAR_15) ||
(isValidRequest.mode === RestoreMode.REGULAR_24)
) {
return AdaApi.isValidMnemonic({ mnemonic, numberOfWords });
const { mnemonic, mode } = isValidRequest;
if (isValidRequest.mode.extra === 'paper') {
return AdaApi.prototype.isValidPaperMnemonic({ mnemonic, numberOfWords: mode.length });
}
return AdaApi.prototype.isValidPaperMnemonic({ mnemonic, numberOfWords });
return AdaApi.isValidMnemonic({ mnemonic, numberOfWords: mode.length });
},
},
daedalusTransfer: {
Expand Down
11 changes: 5 additions & 6 deletions app/containers/transfer/YoroiPlatePage.js
Expand Up @@ -14,7 +14,6 @@ import {
import type { PlateResponse } from '../../api/common/lib/crypto/plate';
import { TransferKind, TransferSource } from '../../types/TransferTypes';
import { generatePlates } from '../../stores/toplevel/WalletRestoreStore';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';
import { SelectedExplorer } from '../../domain/SelectedExplorer';
import type { TransferKindType, TransferSourceType, } from '../../types/TransferTypes';
import type { Notification } from '../../types/notificationType';
Expand Down Expand Up @@ -46,19 +45,19 @@ export default class YoroiPlatePage extends Component<Props> {

const getRestoreMode = () => {
if (yoroiTransfer.transferKind === TransferKind.PAPER) {
return RestoreMode.PAPER;
return { type: 'bip44', extra: 'paper', length: 21 };
}
if (yoroiTransfer.transferSource === TransferSource.BYRON) {
return RestoreMode.REGULAR_15;
return { type: 'bip44', extra: undefined, length: 15 };
}
if (yoroiTransfer.transferSource === TransferSource.JORMUNGANDR_UTXO) {
return RestoreMode.REGULAR_15;
return { type: 'cip1852', extra: undefined, length: 15 };
}
if (yoroiTransfer.transferSource === TransferSource.JORMUNGANDR_CHIMERIC_ACCOUNT) {
return RestoreMode.REGULAR_15;
return { type: 'cip1852', extra: undefined, length: 15 };
}
if (yoroiTransfer.transferSource === TransferSource.SHELLEY) {
return RestoreMode.REGULAR_24;
return { type: 'cip1852', extra: undefined, length: 15 };
}
throw new Error(`${nameof(YoroiPlatePage)} unknown mode`);
};
Expand Down
11 changes: 4 additions & 7 deletions app/containers/transfer/YoroiTransferPage.js
Expand Up @@ -30,7 +30,7 @@ import type { $npm$ReactIntl$IntlFormat } from 'react-intl';
import type { GeneratedData as YoroiPlateData } from './YoroiPlatePage';
import { SelectedExplorer } from '../../domain/SelectedExplorer';
import type { UnitOfAccountSettingType } from '../../types/unitOfAccountType';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';
import type { RestoreModeType } from '../../actions/common/wallet-restore-actions';
import { ApiOptions, getApiMeta, getApiForNetwork, } from '../../api/common/utils';

// Stay this long on the success page, then jump to the wallet transactions page
Expand Down Expand Up @@ -163,8 +163,7 @@ export default class YoroiTransferPage extends Component<InjectedOrGenerated<Gen
onBack={this.backToUninitialized}
mnemonicValidator={mnemonic => this.generated.stores.walletRestore.isValidMnemonic({
mnemonic,
numberOfWords: config.wallets.WALLET_RECOVERY_PHRASE_WORD_COUNT,
mode: RestoreMode.REGULAR_15,
mode: { type: 'bip44', extra: undefined, length: config.wallets.WALLET_RECOVERY_PHRASE_WORD_COUNT },
})}
validWords={validWords}
mnemonicLength={config.wallets.WALLET_RECOVERY_PHRASE_WORD_COUNT}
Expand All @@ -178,8 +177,7 @@ export default class YoroiTransferPage extends Component<InjectedOrGenerated<Gen
onBack={this.backToUninitialized}
mnemonicValidator={mnemonic => this.generated.stores.walletRestore.isValidMnemonic({
mnemonic,
numberOfWords: config.wallets.YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT,
mode: RestoreMode.PAPER,
mode: { type: 'bip44', extra: 'paper', length: config.wallets.YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT },
})}
validWords={validWords}
mnemonicLength={config.wallets.YOROI_PAPER_RECOVERY_PHRASE_WORD_COUNT}
Expand Down Expand Up @@ -339,8 +337,7 @@ export default class YoroiTransferPage extends Component<InjectedOrGenerated<Gen
selectedAccount: number,
isValidMnemonic: ({|
mnemonic: string,
numberOfWords: number,
mode: $PropertyType<typeof RestoreMode, 'REGULAR_15'> | $PropertyType<typeof RestoreMode, 'REGULAR_24'> | $PropertyType<typeof RestoreMode, 'PAPER'>,
mode: RestoreModeType,
|}) => boolean,
|},
wallets: {|
Expand Down
12 changes: 4 additions & 8 deletions app/containers/transfer/YoroiTransferPage.stories.js
Expand Up @@ -30,7 +30,6 @@ import {
WalletChangedError,
} from '../../stores/toplevel/YoroiTransferStore';
import AdaApi from '../../api/ada/index';
import { RestoreMode } from '../../actions/common/wallet-restore-actions';
import {
HARD_DERIVATION_START,
} from '../../config/numbersConfig';
Expand Down Expand Up @@ -61,15 +60,12 @@ const genBaseProps: {|
walletRestore: {
selectedAccount: 0 + HARD_DERIVATION_START,
isValidMnemonic: (isValidRequest) => {
const { mnemonic, numberOfWords } = isValidRequest;
const { mnemonic, mode } = isValidRequest;

if (
(isValidRequest.mode === RestoreMode.REGULAR_15) ||
(isValidRequest.mode === RestoreMode.REGULAR_24)
) {
return AdaApi.isValidMnemonic({ mnemonic, numberOfWords });
if (isValidRequest.mode.extra === 'paper') {
return AdaApi.prototype.isValidPaperMnemonic({ mnemonic, numberOfWords: mode.length });
}
return AdaApi.prototype.isValidPaperMnemonic({ mnemonic, numberOfWords });
return AdaApi.isValidMnemonic({ mnemonic, numberOfWords: mode.length });
},
},
wallets: {
Expand Down

0 comments on commit 73566ea

Please sign in to comment.