import React, { useCallback, useMemo } from 'react'; import { Just } from 'folktale/maybe'; import * as bip32 from 'bip32'; import { times } from 'lodash'; import TrezorConnect from 'trezor-connect'; import * as secp256k1 from 'secp256k1'; import { Text, Grid, H5, CheckboxInput, SelectInput } from 'indigo-react'; import { useWallet } from 'store/wallet'; import { TREZOR_PATH } from 'lib/trezor'; import { WALLET_TYPES } from 'lib/wallet'; import useLoginView from 'lib/useLoginView'; import { getAuthToken } from 'lib/authToken'; import { composeValidator, buildCheckboxValidator, buildSelectValidator, buildHdPathValidator, } from 'form/validators'; import BridgeForm from 'form/BridgeForm'; import Condition from 'form/Condition'; import FormError from 'form/FormError'; import SubmitButton from 'form/SubmitButton'; import { FORM_ERROR } from 'final-form'; import { HdPathInput } from 'form/Inputs'; const ACCOUNT_OPTIONS = times(20, i => ({ text: `Account #${i + 1}`, value: i, })); // see Ledger.js for context — Trezor is basicaly Ledger with less complexity export default function Trezor({ className, goHome }) { useLoginView(WALLET_TYPES.TREZOR); const { setWallet, setWalletHdPath, setAuthToken } = useWallet(); const validate = useMemo( () => composeValidator({ useCustomPath: buildCheckboxValidator(), account: buildSelectValidator(ACCOUNT_OPTIONS), hdPath: buildHdPathValidator(), }), [] ); const onSubmit = useCallback( async values => { TrezorConnect.manifest({ email: 'bridge-trezor@urbit.org', appUrl: 'https://github.com/urbit/bridge', }); const { success, payload } = await TrezorConnect.getPublicKey({ path: values.hdPath, }); if (!success) { return { [FORM_ERROR]: 'Failed to authenticate with your Trezor.' }; } const publicKey = Buffer.from(payload.publicKey, 'hex'); const chainCode = Buffer.from(payload.chainCode, 'hex'); const pub = secp256k1.publicKeyConvert(publicKey, true); const hd = bip32.fromPublicKey(pub, chainCode); const authToken = await getAuthToken({ wallet: hd, walletType: WALLET_TYPES.TREZOR, walletHdPath: values.hdPath, }); setAuthToken(Just(authToken)); setWallet(Just(hd)); setWalletHdPath(values.hdPath); }, [setAuthToken, setWallet, setWalletHdPath] ); const onValues = useCallback(({ valid, values, form }) => { if (!valid) { return; } if (!values.useCustomPath) { form.change('hdPath', TREZOR_PATH.replace(/x/g, values.account)); } }, []); const initialValues = useMemo( () => ({ useCustomPath: false, hdPath: TREZOR_PATH.replace(/x/g, ACCOUNT_OPTIONS[0].value), account: ACCOUNT_OPTIONS[0].value, }), [] ); return ( Connect and authenticate to your Trezor. If you'd like to use a custom derivation path, you may enter it below. Upon login, you will prompted to sign the message "Bridge Authentication Token". This allows Bridge to operate correctly. Never sign this message outside of Bridge. {({ handleSubmit, submitting }) => ( <> {!submitting && 'Authenticate'} {submitting && 'Please check your device'} )} ); }