Mnemonic.tsx
  1  import React, { PureComponent } from 'react';
  2  import { connect } from 'react-redux';
  3  import { mnemonicToSeed, validateMnemonic } from 'bip39';
  4  
  5  import { InsecureWalletName } from 'config';
  6  import translate, { translateRaw } from 'translations';
  7  import { formatMnemonic } from 'utils/formatters';
  8  import { AppState } from 'features/reducers';
  9  import { getSingleDPath, getPaths } from 'features/config';
 10  import { TogglablePassword } from 'components';
 11  import { Input } from 'components/ui';
 12  import DeterministicWalletsModal from './DeterministicWalletsModal';
 13  
 14  interface OwnProps {
 15    onUnlock(param: any): void;
 16  }
 17  
 18  interface StateProps {
 19    dPath: DPath;
 20    dPaths: DPath[];
 21  }
 22  
 23  type Props = OwnProps & StateProps;
 24  
 25  interface State {
 26    phrase: string;
 27    formattedPhrase: string;
 28    pass: string;
 29    seed: string;
 30    dPath: DPath;
 31  }
 32  
 33  class MnemonicDecryptClass extends PureComponent<Props, State> {
 34    public state: State = {
 35      phrase: '',
 36      formattedPhrase: '',
 37      pass: '',
 38      seed: '',
 39      dPath: this.props.dPath
 40    };
 41  
 42    public UNSAFE_componentWillReceiveProps(nextProps: Props) {
 43      if (this.props.dPath !== nextProps.dPath) {
 44        this.setState({ dPath: nextProps.dPath });
 45      }
 46    }
 47  
 48    public render() {
 49      const { phrase, formattedPhrase, seed, dPath, pass } = this.state;
 50      const isValidMnemonic = validateMnemonic(formattedPhrase);
 51  
 52      return (
 53        <React.Fragment>
 54          <div id="selectedTypeKey">
 55            <div className="form-group">
 56              <TogglablePassword
 57                value={phrase}
 58                rows={4}
 59                placeholder={translateRaw('X_MNEMONIC')}
 60                isValid={isValidMnemonic}
 61                isTextareaWhenVisible={true}
 62                onChange={this.onMnemonicChange}
 63                onEnter={isValidMnemonic ? this.onDWModalOpen : undefined}
 64              />
 65            </div>
 66            <div className="form-group">
 67              <p>{translate('ADD_LABEL_8')}</p>
 68              <Input
 69                isValid={true}
 70                showValidAsPlain={true}
 71                value={pass}
 72                onChange={this.onPasswordChange}
 73                placeholder={translateRaw('INPUT_PASSWORD_LABEL')}
 74                type="password"
 75              />
 76            </div>
 77            <div className="form-group">
 78              <button
 79                style={{ width: '100%' }}
 80                onClick={this.onDWModalOpen}
 81                className="btn btn-primary btn-lg"
 82                disabled={!isValidMnemonic}
 83              >
 84                {translate('MNEMONIC_CHOOSE_ADDR')}
 85              </button>
 86            </div>
 87          </div>
 88  
 89          <DeterministicWalletsModal
 90            isOpen={!!seed}
 91            seed={seed}
 92            dPath={dPath}
 93            dPaths={this.props.dPaths}
 94            onCancel={this.handleCancel}
 95            onConfirmAddress={this.handleUnlock}
 96            onPathChange={this.handlePathChange}
 97          />
 98        </React.Fragment>
 99      );
100    }
101  
102    public onPasswordChange = (e: React.FormEvent<HTMLInputElement>) => {
103      this.setState({ pass: e.currentTarget.value });
104    };
105  
106    public onMnemonicChange = (e: React.FormEvent<HTMLTextAreaElement>) => {
107      const phrase = e.currentTarget.value;
108      const formattedPhrase = formatMnemonic(phrase);
109  
110      this.setState({
111        phrase,
112        formattedPhrase
113      });
114    };
115  
116    public onDWModalOpen = () => {
117      const { formattedPhrase, pass } = this.state;
118  
119      if (!validateMnemonic(formattedPhrase)) {
120        return;
121      }
122  
123      try {
124        const seed = mnemonicToSeed(formattedPhrase, pass).toString('hex');
125        this.setState({ seed });
126      } catch (err) {
127        console.log(err);
128      }
129    };
130  
131    private handleCancel = () => {
132      this.setState({ seed: '' });
133    };
134  
135    private handlePathChange = (dPath: DPath) => {
136      this.setState({ dPath });
137    };
138  
139    private handleUnlock = (address: string, index: number) => {
140      const { formattedPhrase, pass, dPath } = this.state;
141  
142      this.props.onUnlock({
143        path: `${dPath.value}/${index}`,
144        pass,
145        phrase: formattedPhrase,
146        address
147      });
148  
149      this.setState({
150        seed: '',
151        pass: '',
152        phrase: '',
153        formattedPhrase: ''
154      });
155    };
156  }
157  
158  function mapStateToProps(state: AppState): StateProps {
159    return {
160      // Mnemonic dPath is guaranteed to always be provided
161      dPath: getSingleDPath(state, InsecureWalletName.MNEMONIC_PHRASE) as DPath,
162      dPaths: getPaths(state, InsecureWalletName.MNEMONIC_PHRASE)
163    };
164  }
165  
166  export const MnemonicDecrypt = connect(mapStateToProps)(MnemonicDecryptClass);