on master help sign in
Merge pull request #13 from urbit/jt-hardware-path
98d9916Will Kim
src/Bridge.js+11−4
@@ -9,7 +9,7 @@ import { Container, Row, Col } from './components/Base'
9
9
10import { ROUTE_NAMES, router } from './lib/router'
10import { ROUTE_NAMES, router } from './lib/router'
11import { NETWORK_NAMES } from './lib/network'
11import { NETWORK_NAMES } from './lib/network'
12−import { WALLET_NAMES } from './lib/wallet'
12+import { WALLET_NAMES, DEFAULT_HD_PATH } from './lib/wallet'
13import { BRIDGE_ERROR } from './lib/error'
13import { BRIDGE_ERROR } from './lib/error'
14
14
15class Bridge extends React.Component {
15class Bridge extends React.Component {
@@ -27,6 +27,7 @@ class Bridge extends React.Component {
27 // wallet
27 // wallet
28 walletType: WALLET_NAMES.MNEMONIC,
28 walletType: WALLET_NAMES.MNEMONIC,
29 wallet: Maybe.Nothing(),
29 wallet: Maybe.Nothing(),
30+ walletHdPath: DEFAULT_HD_PATH,
30 // urbit wallet-related
31 // urbit wallet-related
31 urbitWallet: Maybe.Nothing(),
32 urbitWallet: Maybe.Nothing(),
32 authMnemonic: Maybe.Nothing(),
33 authMnemonic: Maybe.Nothing(),
@@ -44,6 +45,7 @@ class Bridge extends React.Component {
44 this.setNetwork = this.setNetwork.bind(this)
45 this.setNetwork = this.setNetwork.bind(this)
45 this.setWalletType = this.setWalletType.bind(this)
46 this.setWalletType = this.setWalletType.bind(this)
46 this.setWallet = this.setWallet.bind(this)
47 this.setWallet = this.setWallet.bind(this)
48+ this.setWalletHdPath = this.setWalletHdPath.bind(this)
47 this.setUrbitWallet = this.setUrbitWallet.bind(this)
49 this.setUrbitWallet = this.setUrbitWallet.bind(this)
48 this.setAuthMnemonic = this.setAuthMnemonic.bind(this)
50 this.setAuthMnemonic = this.setAuthMnemonic.bind(this)
49 this.setPointCursor = this.setPointCursor.bind(this)
51 this.setPointCursor = this.setPointCursor.bind(this)
@@ -142,9 +144,11 @@ class Bridge extends React.Component {
142 }
144 }
143
145
144 setWallet(wallet) {
146 setWallet(wallet) {
145− this.setState({
147+ this.setState({ wallet })
146− wallet: wallet
148+ }
147− })
149+
150+ setWalletHdPath(walletHdPath) {
151+ this.setState({ walletHdPath })
148 }
152 }
149
153
150 setUrbitWallet(urbitWallet) {
154 setUrbitWallet(urbitWallet) {
@@ -176,6 +180,7 @@ class Bridge extends React.Component {
176 routeCrumbs,
180 routeCrumbs,
177 networkType,
181 networkType,
178 walletType,
182 walletType,
183+ walletHdPath,
179 wallet,
184 wallet,
180 urbitWallet,
185 urbitWallet,
181 authMnemonic,
186 authMnemonic,
@@ -214,8 +219,10 @@ class Bridge extends React.Component {
214 contracts={ contracts }
219 contracts={ contracts }
215 // wallet
220 // wallet
216 setWalletType={ this.setWalletType }
221 setWalletType={ this.setWalletType }
222+ setWalletHdPath={ this.setWalletHdPath }
217 setWallet={ this.setWallet }
223 setWallet={ this.setWallet }
218 walletType={ walletType }
224 walletType={ walletType }
225+ walletHdPath={ walletHdPath }
219 wallet={ wallet }
226 wallet={ wallet }
220 // urbit wallet
227 // urbit wallet
221 urbitWallet={ urbitWallet }
228 urbitWallet={ urbitWallet }
src/components/StatelessTransaction.js+1
@@ -113,6 +113,7 @@ const StatelessTransaction = props => {
113 onClick={ () => signTransaction({
113 onClick={ () => signTransaction({
114 wallet: props.wallet,
114 wallet: props.wallet,
115 walletType: props.walletType,
115 walletType: props.walletType,
116+ walletHdPath: props.walletHdPath,
116 txn: props.txn,
117 txn: props.txn,
117 setStx: props.setStx,
118 setStx: props.setStx,
118 nonce: props.nonce,
119 nonce: props.nonce,
src/components/Transaction/Signer.js+4−3
@@ -76,7 +76,8 @@ class Signer extends React.Component {
76 }
76 }
77
77
78 async signTransaction(signingArgs) {
78 async signTransaction(signingArgs) {
79+ const { wallet, walletType, txn, walletHdPath } = this.props
79− const { wallet, walletType, txn, setSignedTransaction } = this.props
80+ const { setSignedTransaction } = this.props
80
81
81 let { nonce, chainId, gasPrice, gasLimit } = signingArgs
82 let { nonce, chainId, gasPrice, gasLimit } = signingArgs
82 nonce = this.toHex(nonce)
83 nonce = this.toHex(nonce)
@@ -104,9 +105,9 @@ class Signer extends React.Component {
104 const stx = new Tx(utx)
105 const stx = new Tx(utx)
105
106
106 if (walletType === WALLET_NAMES.LEDGER) {
107 if (walletType === WALLET_NAMES.LEDGER) {
107− await ledgerSignTransaction(stx)
108+ await ledgerSignTransaction(stx, walletHdPath)
108 } else if (walletType === WALLET_NAMES.TREZOR) {
109 } else if (walletType === WALLET_NAMES.TREZOR) {
109− await trezorSignTransaction(stx)
110+ await trezorSignTransaction(stx, walletHdPath)
110 } else {
111 } else {
111 stx.sign(sec)
112 stx.sign(sec)
112 }
113 }
src/components/Transaction/StatelessSigner.jsdeleted−194
@@ -1,194 +0,0 @@
1−import Tx from 'ethereumjs-tx'
2−import React from 'react'
3−import Web3 from 'web3'
4−import { Button } from '../Base'
5−import { Input, InnerLabel } from '../Base'
6−
7−import { ledgerSignTransaction } from '../../lib/ledger'
8−import { WALLET_NAMES, addressFromSecp256k1Public } from '../../lib/wallet'
9−import { BRIDGE_ERROR } from '../../lib/error'
10−
11−class Signer extends React.Component {
12− constructor(props) {
13− super(props)
14−
15− // this.state = {
16− // nonce: '',
17− // gasPrice: '5',
18− // chainId: '',
19− // gasLimit: '600000'
20− // }
21−
22− const dummy = new Web3()
23− this.toHex = dummy.utils.toHex
24− this.toWei = dummy.utils.toWei
25− this.fromWei = dummy.utils.fromWei
26−
27− this.handleNonceInput = this.handleNonceInput.bind(this)
28− this.handleChainIdInput = this.handleChainIdInput.bind(this)
29− this.handleGasPriceInput = this.handleGasPriceInput.bind(this)
30− this.handleGasLimitInput = this.handleGasLimitInput.bind(this)
31− this.signTransaction = this.signTransaction.bind(this)
32− }
33−
34− componentDidMount() {
35− const { web3, wallet } = this.props
36−
37− const addr = wallet.matchWith({
38− Just: (wal) => addressFromSecp256k1Public(wal.value.publicKey),
39− Nothing: () => {
40− throw BRIDGE_ERROR.MISSING_WALLET
41− }
42− })
43−
44− web3.map(w3 =>
45− this.getTxnInfo(w3, addr)
46− .then(txInfo => this.setState(txInfo))
47− )
48− }
49−
50− getTxnInfo = async (web3, addr) => {
51− let nonce = await web3.eth.getTransactionCount(addr)
52− let chainId = await web3.eth.net.getId()
53− let gasPrice = await web3.eth.getGasPrice()
54− return {
55− nonce: nonce,
56− chainId: chainId,
57− gasPrice: this.fromWei(gasPrice, 'gwei')
58− }
59− }
60−
61− handleNonceInput(nonce) {
62− this.setState({ nonce })
63− }
64−
65− handleChainIdInput(chainId) {
66− this.setState({ chainId })
67− }
68−
69− handleGasPriceInput(gasPrice) {
70− this.setState({ gasPrice })
71− }
72−
73− handleGasLimitInput(gasLimit) {
74− this.setState({ gasLimit })
75− }
76−
77− async signTransaction(signingArgs) {
78− const { wallet, walletType, txn, setSignedTransaction } = this.props
79−
80− let { nonce, chainId, gasPrice, gasLimit } = signingArgs
81− nonce = this.toHex(nonce)
82− chainId = this.toHex(chainId)
83− gasPrice = this.toHex(this.toWei(gasPrice, 'gwei'))
84− gasLimit = this.toHex(gasLimit)
85−
86− const wal = wallet.matchWith({
87− Just: (w) => w.value,
88− Nothing: () => {
89− throw BRIDGE_ERROR.MISSING_WALLET
90− }
91− })
92−
93− const sec = wal.privateKey
94−
95− const utx = txn.matchWith({
96− Just: (tx) =>
97− Object.assign(tx.value, { nonce, chainId, gasPrice, gasLimit }),
98− Nothing: () => {
99− throw BRIDGE_ERROR.MISSING_TXN
100− }
101− })
102−
103− const stx = new Tx(utx)
104−
105− if (walletType === WALLET_NAMES.LEDGER) {
106− await ledgerSignTransaction(stx)
107− } else {
108− stx.sign(sec)
109− }
110−
111− setSignedTransaction(stx)
112− }
113−
114− render() {
115−
116− const { state, props } = this
117−
118− const signingArgs = {
119− nonce: props.nonce,
120− gasPrice: props.gasPrice,
121− gasLimit: props.gasLimit,
122− chainId: props.chainId,
123− }
124−
125−
126−
127− return (
128− <React.Fragment>
129− <Input
130− className='mono mt-4'
131− prop-size='md'
132− prop-format='innerLabel'
133− value={ props.gasPrice }
134− onChange={ v => this.handleGasPriceInput(v) }>
135− <InnerLabel>
136− { 'Gas Price (gwei)' }
137− </InnerLabel>
138− </Input>
139−
140− <Input
141− className='mono mt-4'
142− prop-size='md'
143− prop-format='innerLabel'
144− value={ props.gasLimit }
145− onChange={ v => this.handleGasLimitInput(v) }>
146− <InnerLabel>
147− { 'Gas Limit' }
148− </InnerLabel>
149− </Input>
150−
151− {
152− props.web3.matchWith({
153− Just: () => <div />,
154− Nothing: () =>
155− <React.Fragment>
156− <Input
157− className='mono'
158− prop-size='md'
159− prop-format='innerLabel'
160− value={ props.nonce }
161− onChange={ v => this.handleNonceInput(v) }>
162− <InnerLabel>
163− { 'Nonce' }
164− </InnerLabel>
165− </Input>
166−
167− <Input
168− className='mono mt-4 mb-8'
169− prop-size='md'
170− prop-format='innerLabel'
171− value={ props.chainId }
172− onChange={ v => this.handleChainIdInput(v) }>
173− <InnerLabel>
174− { 'Chain ID' }
175− </InnerLabel>
176− </Input>
177−
178− </React.Fragment>
179− })
180− }
181−
182− <Button
183− disabled={ props.disabled }
184− prop-size={'lg wide'}
185− prop-color={this.props['prop-color']}
186− onClick={ () => this.signTransaction(signingArgs) }>
187− { 'Sign Transaction' }
188− </Button>
189− </React.Fragment>
190− )
191− }
192−}
193−
194−export default Signer
src/lib/ledger.js+8−2
@@ -3,12 +3,18 @@ import Eth from '@ledgerhq/hw-app-eth'
3
3
4const LEDGER_BASE_PATH = "44'/60'/0'/0/0"
4const LEDGER_BASE_PATH = "44'/60'/0'/0/0"
5
5
6+const chopHdPrefix = str =>
7+ str.slice(0, 2) === "m/"
8+ ? str.slice(2)
9+ : str
10+
6−const ledgerSignTransaction = async txn => {
11+const ledgerSignTransaction = async (txn, hdpath) => {
7 const transport = await Transport.create()
12 const transport = await Transport.create()
8 const eth = new Eth(transport)
13 const eth = new Eth(transport)
14+ const path = chopHdPrefix(hdpath)
9
15
10 const serializedTx = txn.serialize().toString('hex')
16 const serializedTx = txn.serialize().toString('hex')
11− const sig = await eth.signTransaction(LEDGER_BASE_PATH, serializedTx)
17+ const sig = await eth.signTransaction(path, serializedTx)
12
18
13 txn.v = Buffer.from(sig.v, 'hex')
19 txn.v = Buffer.from(sig.v, 'hex')
14 txn.r = Buffer.from(sig.r, 'hex')
20 txn.r = Buffer.from(sig.r, 'hex')
src/lib/trezor.js+2−2
@@ -7,7 +7,7 @@ const formatChainId = val =>
7 ? val
7 ? val
8 : parseInt(val.slice(2), 16) // assume 0x-prefixed hex string
8 : parseInt(val.slice(2), 16) // assume 0x-prefixed hex string
9
9
10−const trezorSignTransaction = async txn => {
10+const trezorSignTransaction = async (txn, hdpath) => {
11 const trezorFormattedTxn = {
11 const trezorFormattedTxn = {
12 to: txn.to.toString('hex'),
12 to: txn.to.toString('hex'),
13 value: txn.value.toString('hex'),
13 value: txn.value.toString('hex'),
@@ -19,7 +19,7 @@ const trezorSignTransaction = async txn => {
19 }
19 }
20
20
21 const sig = await TrezorConnect.ethereumSignTransaction({
21 const sig = await TrezorConnect.ethereumSignTransaction({
22− path: TREZOR_BASE_PATH,
22+ path: hdpath,
23 transaction: trezorFormattedTxn
23 transaction: trezorFormattedTxn
24 })
24 })
25
25
src/lib/txn.js+5−1
@@ -6,6 +6,7 @@ import Web3 from 'web3'
6
6
7import { BRIDGE_ERROR } from '../lib/error'
7import { BRIDGE_ERROR } from '../lib/error'
8import { ledgerSignTransaction } from '../lib/ledger'
8import { ledgerSignTransaction } from '../lib/ledger'
9+import { trezorSignTransaction } from '../lib/trezor'
9import {
10import {
10 WALLET_NAMES,
11 WALLET_NAMES,
11 addHexPrefix
12 addHexPrefix
@@ -76,6 +77,7 @@ const signTransaction = async config => {
76 let {
77 let {
77 wallet,
78 wallet,
78 walletType,
79 walletType,
80+ walletHdPath,
79 txn,
81 txn,
80 setStx,
82 setStx,
81 nonce,
83 nonce,
@@ -109,7 +111,9 @@ const signTransaction = async config => {
109 const stx = new Tx(utx)
111 const stx = new Tx(utx)
110
112
111 if (walletType === WALLET_NAMES.LEDGER) {
113 if (walletType === WALLET_NAMES.LEDGER) {
112− await ledgerSignTransaction(stx)
114+ await ledgerSignTransaction(stx, walletHdPath)
115+ } else if (walletType === WALLET_NAMES.TREZOR) {
116+ await trezorSignTransaction(stx, walletHdPath)
113 } else {
117 } else {
114 stx.sign(sec)
118 stx.sign(sec)
115 }
119 }
src/views/AcceptTransfer.js+1
@@ -303,6 +303,7 @@ class AcceptTransfer extends React.Component {
303 contracts={props.contracts}
303 contracts={props.contracts}
304 wallet={props.wallet}
304 wallet={props.wallet}
305 walletType={props.walletType}
305 walletType={props.walletType}
306+ walletHdPath={props.walletHdPath}
306 // Tx
307 // Tx
307 txn={state.txn}
308 txn={state.txn}
308 stx={state.stx}
309 stx={state.stx}
src/views/CancelTransfer.js+1
@@ -234,6 +234,7 @@ class CancelTransfer extends React.Component {
234 contracts={props.contracts}
234 contracts={props.contracts}
235 wallet={props.wallet}
235 wallet={props.wallet}
236 walletType={props.walletType}
236 walletType={props.walletType}
237+ walletHdPath={props.walletHdPath}
237 // Tx
238 // Tx
238 txn={state.txn}
239 txn={state.txn}
239 stx={state.stx}
240 stx={state.stx}
src/views/CreateGalaxy.js+1
@@ -250,6 +250,7 @@ class CreateGalaxy extends React.Component {
250 contracts={props.contracts}
250 contracts={props.contracts}
251 wallet={props.wallet}
251 wallet={props.wallet}
252 walletType={props.walletType}
252 walletType={props.walletType}
253+ walletHdPath={props.walletHdPath}
253 // Tx
254 // Tx
254 txApproval={state.txApproval}
255 txApproval={state.txApproval}
255 txn={state.txn}
256 txn={state.txn}
src/views/IssueChild.js+1
@@ -461,6 +461,7 @@ class IssueChild extends React.Component {
461 contracts={props.contracts}
461 contracts={props.contracts}
462 wallet={props.wallet}
462 wallet={props.wallet}
463 walletType={props.walletType}
463 walletType={props.walletType}
464+ walletHdPath={props.walletHdPath}
464 // Tx
465 // Tx
465 txn={state.txn}
466 txn={state.txn}
466 stx={state.stx}
467 stx={state.stx}
src/views/Ledger.js+81−41
@@ -3,6 +3,7 @@ import React from 'react'
3import Maybe from 'folktale/maybe'
3import Maybe from 'folktale/maybe'
4import { Button } from '../components/Base'
4import { Button } from '../components/Base'
5import { Row, Col, H1, P, H2 } from '../components/Base'
5import { Row, Col, H1, P, H2 } from '../components/Base'
6+import { Form, Input, InnerLabel } from '../components/Base'
6import Transport from '@ledgerhq/hw-transport-u2f'
7import Transport from '@ledgerhq/hw-transport-u2f'
7import Eth from '@ledgerhq/hw-app-eth'
8import Eth from '@ledgerhq/hw-app-eth'
8import * as secp256k1 from 'secp256k1'
9import * as secp256k1 from 'secp256k1'
@@ -10,58 +11,78 @@ import * as secp256k1 from 'secp256k1'
10import { LEDGER_BASE_PATH } from '../lib/ledger'
11import { LEDGER_BASE_PATH } from '../lib/ledger'
11import { ROUTE_NAMES } from '../lib/router'
12import { ROUTE_NAMES } from '../lib/router'
12
13
14+const chopHdPrefix = str =>
15+ str.slice(0, 2) === "m/"
16+ ? str.slice(2)
17+ : str
18+
19+const addHdPrefix = str =>
20+ str.slice(0, 2) === "m/"
21+ ? str
22+ : "m/" + str
23+
13class Ledger extends React.Component {
24class Ledger extends React.Component {
25+ constructor(props) {
26+ super(props)
14
27
15− componentDidMount() {
28+ this.state = {
16− this.pollDevice()
29+ hdpath: LEDGER_BASE_PATH
30+ }
17
31
18− this.pollerId = setInterval(
19− () => this.pollDevice(),
32+ this.pollDevice = this.pollDevice.bind(this)
20− 500
33+ this.handleHdPathInput = this.handleHdPathInput.bind(this)
21− )
22 }
34 }
23
35
24− componentWillUnmount() {
36+ handleHdPathInput(hdpath) {
25− clearInterval(this.pollerId)
37+ this.setState({ hdpath })
26 }
38 }
27
39
28 async pollDevice() {
40 async pollDevice() {
29− const { wallet, setWallet } = this.props
41+ const { setWallet, setWalletHdPath } = this.props
30−
42+ const { hdpath } = this.state
31− if (Maybe.Nothing.hasInstance(wallet)) {
43+
32− const transport = await Transport.create()
44+ const transport = await Transport.create()
33− const eth = new Eth(transport)
45+ const eth = new Eth(transport)
34−
46+ const path = chopHdPrefix(hdpath)
47+
35− eth.getAddress(LEDGER_BASE_PATH, false, true)
48+ eth.getAddress(path, false, true)
36− .then(info => {
49+ .then(info => {
37− const publicKey = Buffer.from(info.publicKey, 'hex')
50+ const publicKey = Buffer.from(info.publicKey, 'hex')
38− const chainCode = Buffer.from(info.chainCode, 'hex')
51+ const chainCode = Buffer.from(info.chainCode, 'hex')
39− const pub = secp256k1.publicKeyConvert(publicKey, true)
52+ const pub = secp256k1.publicKeyConvert(publicKey, true)
40− const hd = bip32.fromPublicKey(pub, chainCode)
53+ const hd = bip32.fromPublicKey(pub, chainCode)
41− setWallet(Maybe.Just(hd))
54+ setWallet(Maybe.Just(hd))
55+ setWalletHdPath(addHdPrefix(hdpath))
42− }, _ => {
56+ }, _ => {
43− setWallet(Maybe.Nothing())
57+ setWallet(Maybe.Nothing())
44− })
58+ })
45− }
46 }
59 }
47
60
48 render() {
61 render() {
49 const { pushRoute, popRoute, wallet } = this.props
62 const { pushRoute, popRoute, wallet } = this.props
63+ const { hdpath } = this.state
50
64
51 return (
65 return (
52
66
53 <Row>
67 <Row>
54− <Col>
68+ <Col className={'measure-md'}>
55 <H1>{ 'Authenticate With Your Ledger' }</H1>
69 <H1>{ 'Authenticate With Your Ledger' }</H1>
56
70
57 <H2>{'Running on HTTPS?'}</H2>
71 <H2>{'Running on HTTPS?'}</H2>
58
72
59 <P>
73 <P>
60 { `Connect and authenticate to your Ledger, and then open the
74 { `Connect and authenticate to your Ledger, and then open the
61− "Ethereum" application. If you're running on older firmware, make sure the browser
75+ "Ethereum" application. If you're running on older firmware,
62− support" option is turned on.` }
76+ make sure the "browser support" option is turned on. To sign
77+ transactions, you'll also need to enable the "contract data"
78+ option.` }
63 </P>
79 </P>
64
80
81+ <P>
82+ { `If you'd like to use a custom derivation path, you may enter
83+ it below.`
84+ }
85+ </P>
65
86
66 <H2>{'Running on HTTP?'}</H2>
87 <H2>{'Running on HTTP?'}</H2>
67
88
@@ -78,19 +99,38 @@ class Ledger extends React.Component {
78 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
99 <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
79 </ol>
100 </ol>
80
101
81−
102+ <Form>
103+ <Input
104+ className='pt-8 mt-8'
105+ prop-size='md'
106+ prop-format='innerLabel'
107+ name='hdpath'
108+ value={ addHdPrefix(hdpath) }
109+ autocomplete='off'
110+ onChange={ this.handleHdPathInput }>
111+ <InnerLabel>{'HD Path'}</InnerLabel>
112+ </Input>
113+
82− <Button
114+ <Button
115+ prop-size={'wide lg'}
83− className={'mt-8'}
116+ className={'mt-8'}
117+ onClick={ this.pollDevice }>
118+ { 'Authenticate →' }
119+ </Button>
120+
121+ <Button
122+ className={'mt-8'}
123+ prop-size={'wide lg'}
84− disabled={ Maybe.Nothing.hasInstance(wallet) }
124+ disabled={ Maybe.Nothing.hasInstance(wallet) }
85− onClick={
125+ onClick={
86− () => {
126+ () => {
87− popRoute()
127+ popRoute()
88− pushRoute(ROUTE_NAMES.SHIPS)
128+ pushRoute(ROUTE_NAMES.SHIPS)
89− }
129+ }
90− }
130+ }>
91− >
92− { 'Continue →' }
131+ { 'Continue →' }
93− </Button>
132+ </Button>
133+ </Form>
94
134
95 </Col>
135 </Col>
96 </Row>
136 </Row>
src/views/Mnemonic.js+2−1
@@ -59,10 +59,11 @@ class Mnemonic extends React.Component {
59 }
59 }
60
60
61 attemptWalletDerivation(mnemonic, hdpath) {
61 attemptWalletDerivation(mnemonic, hdpath) {
62− const { setWallet, setAuthMnemonic } = this.props
62+ const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props
63 const wallet = walletFromMnemonic(mnemonic, hdpath)
63 const wallet = walletFromMnemonic(mnemonic, hdpath)
64 setWallet(wallet)
64 setWallet(wallet)
65 setAuthMnemonic(Maybe.Just(mnemonic))
65 setAuthMnemonic(Maybe.Just(mnemonic))
66+ setWalletHdPath(hdpath)
66 }
67 }
67
68
68 render() {
69 render() {
src/views/SetProxy.js+1
@@ -320,6 +320,7 @@ class SetProxy extends React.Component {
320 contracts={props.contracts}
320 contracts={props.contracts}
321 wallet={props.wallet}
321 wallet={props.wallet}
322 walletType={props.walletType}
322 walletType={props.walletType}
323+ walletHdPath={props.walletHdPath}
323 // Tx
324 // Tx
324 txn={state.txn}
325 txn={state.txn}
325 stx={state.stx}
326 stx={state.stx}
src/views/Transfer.js+1
@@ -292,6 +292,7 @@ class Transfer extends React.Component {
292 contracts={props.contracts}
292 contracts={props.contracts}
293 wallet={props.wallet}
293 wallet={props.wallet}
294 walletType={props.walletType}
294 walletType={props.walletType}
295+ walletHdPath={props.walletHdPath}
295 // Tx
296 // Tx
296 txn={state.txn}
297 txn={state.txn}
297 stx={state.stx}
298 stx={state.stx}
src/views/Trezor.js+68−36
@@ -3,6 +3,7 @@ import React from 'react'
3import Maybe from 'folktale/maybe'
3import Maybe from 'folktale/maybe'
4import { Button } from '../components/Base'
4import { Button } from '../components/Base'
5import { Row, Col, H1, P } from '../components/Base'
5import { Row, Col, H1, P } from '../components/Base'
6+import { Form, InnerLabel, Input } from '../components/Base'
6import TrezorConnect from 'trezor-connect'
7import TrezorConnect from 'trezor-connect'
7import * as secp256k1 from 'secp256k1'
8import * as secp256k1 from 'secp256k1'
8
9
@@ -11,57 +12,89 @@ import { ROUTE_NAMES } from '../lib/router'
11
12
12class Trezor extends React.Component {
13class Trezor extends React.Component {
13
14
14− componentDidMount() {
15+ constructor(props) {
16+ super(props)
17+
18+ this.state = {
19+ hdpath: TREZOR_BASE_PATH
20+ }
21+
22+ this.handleHdPathInput = this.handleHdPathInput.bind(this)
15− this.pollDevice()
23+ this.pollDevice = this.pollDevice.bind(this)
24+ }
25+
26+ handleHdPathInput(hdpath) {
27+ this.setState({ hdpath })
16 }
28 }
17
29
18 async pollDevice() {
30 async pollDevice() {
19− const { wallet, setWallet } = this.props
31+ const { setWallet, setWalletHdPath } = this.props
20−
32+ const { hdpath } = this.state
21− if (Maybe.Nothing.hasInstance(wallet)) {
33+
22− TrezorConnect.getPublicKey({ path: TREZOR_BASE_PATH })
34+ TrezorConnect.getPublicKey({ path: hdpath })
23− .then(info => {
35+ .then(info => {
24− if (info.success === true) {
36+ if (info.success === true) {
25− const payload = info.payload
37+ const payload = info.payload
26− const publicKey = Buffer.from(payload.publicKey, 'hex')
38+ const publicKey = Buffer.from(payload.publicKey, 'hex')
27− const chainCode = Buffer.from(payload.chainCode, 'hex')
39+ const chainCode = Buffer.from(payload.chainCode, 'hex')
28− const pub = secp256k1.publicKeyConvert(publicKey, true)
40+ const pub = secp256k1.publicKeyConvert(publicKey, true)
29− const hd = bip32.fromPublicKey(pub, chainCode)
41+ const hd = bip32.fromPublicKey(pub, chainCode)
30− setWallet(Maybe.Just(hd))
42+ setWallet(Maybe.Just(hd))
43+ setWalletHdPath(hdpath)
31− } else {
44+ } else {
32− setWallet(Maybe.Nothing())
45+ setWallet(Maybe.Nothing())
33− }
34− })
46+ }
35− }
47+ })
36 }
48 }
37
49
38 render() {
50 render() {
39 const { pushRoute, popRoute, wallet } = this.props
51 const { pushRoute, popRoute, wallet } = this.props
52+ const { hdpath } = this.state
40
53
41 return (
54 return (
42−
43 <Row>
55 <Row>
44− <Col>
56+ <Col className={'measure-md'}>
45 <H1>{ 'Authenticate With Your Trezor' }</H1>
57 <H1>{ 'Authenticate With Your Trezor' }</H1>
46
58
47 <P>
59 <P>
48− { 'Connect and authenticate to your Trezor. If the Trezor ' +
60+ { `Connect and authenticate to your Trezor. If you'd like
49− 'popup window does not display, reconnect your Trezor and ' +
61+ to use a custom derivation path, you may enter it below.`
50− 'navigate back to this page.'
51 }
62 }
52 </P>
63 </P>
53
64
65+ <Form>
66+ <Input
67+ className='pt-8'
68+ prop-size='md'
69+ prop-format='innerLabel'
70+ name='hdpath'
71+ value={ hdpath }
72+ autocomplete='off'
73+ onChange={ this.handleHdPathInput }>
74+ <InnerLabel>{'HD Path'}</InnerLabel>
75+ </Input>
76+
77+ <Button
78+ className={'mt-8'}
79+ prop-size={'wide lg'}
80+ onClick={ this.pollDevice }>
81+ { 'Authenticate →' }
54− <Button
82+ </Button>
83+
84+ <Button
85+ className={'mt-8'}
86+ prop-size={'wide lg'}
55− disabled={ Maybe.Nothing.hasInstance(wallet) }
87+ disabled={ Maybe.Nothing.hasInstance(wallet) }
56− onClick={
88+ onClick={
57− () => {
89+ () => {
58− popRoute()
90+ popRoute()
59− pushRoute(ROUTE_NAMES.SHIPS)
91+ pushRoute(ROUTE_NAMES.SHIPS)
60− }
92+ }
61− }
93+ }>
62− >
63− { 'Continue →' }
94+ { 'Continue →' }
64− </Button>
95+ </Button>
96+
97+ </Form>
65
98
66 </Col>
99 </Col>
67 </Row>
100 </Row>
@@ -70,4 +103,3 @@ class Trezor extends React.Component {
70}
103}
71
104
72export default Trezor
105export default Trezor
73−