on master help sign in
Merge pull request #24 from urbit/jt-alternate-auth
8689d11Will Kim
package-lock.json+44−4
@@ -1,6 +1,6 @@
1{
1{
2 "name": "bridge",
2 "name": "bridge",
3− "version": "0.4.0",
3+ "version": "1.0.1",
4 "lockfileVersion": 1,
4 "lockfileVersion": 1,
5 "requires": true,
5 "requires": true,
6 "dependencies": {
6 "dependencies": {
@@ -1965,9 +1965,9 @@
1965 }
1965 }
1966 },
1966 },
1967 "azimuth-js": {
1967 "azimuth-js": {
1968− "version": "0.8.3",
1968+ "version": "0.10.0",
1969− "resolved": "https://registry.npmjs.org/azimuth-js/-/azimuth-js-0.8.3.tgz",
1969+ "resolved": "https://registry.npmjs.org/azimuth-js/-/azimuth-js-0.10.0.tgz",
1970− "integrity": "sha512-ls668YS6pVwplapzqAk+wfDOLr+DW9QEwN1PwVgsxXqgls1b1pYqqjmREbWHexbTSCbSW31h8JHnUIc6sOf7mQ==",
1970+ "integrity": "sha512-VDg60BYSpTjsv1K/DX7VY5F0HbxHV0X1cSoG3c1iZYH5Lxy8TErYq4CJ+D6er0xtI7JI3hmKOZS+p+ru42sT3A==",
1971 "requires": {
1971 "requires": {
1972 "azimuth-solidity": "1.0.2",
1972 "azimuth-solidity": "1.0.2",
1973 "ethereumjs-util": "^5.2.0",
1973 "ethereumjs-util": "^5.2.0",
@@ -10117,6 +10117,41 @@
10117 "sha3": "^1.2.2"
10117 "sha3": "^1.2.2"
10118 }
10118 }
10119 },
10119 },
10120+ "keythereum": {
10121+ "version": "1.0.4",
10122+ "resolved": "https://registry.npmjs.org/keythereum/-/keythereum-1.0.4.tgz",
10123+ "integrity": "sha512-c3gWM0nQ6x5TKAzTOA1yIqn73S8sP9+lR7mc7QS6t509g7C0/CukykxGA6+B+aXI6BIrlSwVh5muPv/I1lD9LA==",
10124+ "requires": {
10125+ "crypto-browserify": "3.12.0",
10126+ "keccak": "1.4.0",
10127+ "scrypt": "6.0.3",
10128+ "secp256k1": "3.5.0",
10129+ "sjcl": "1.0.6",
10130+ "uuid": "3.0.0"
10131+ },
10132+ "dependencies": {
10133+ "secp256k1": {
10134+ "version": "3.5.0",
10135+ "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-3.5.0.tgz",
10136+ "integrity": "sha512-e5QIJl8W7Y4tT6LHffVcZAxJjvpgE5Owawv6/XCYPQljE9aP2NFFddQ8OYMKhdLshNu88FfL3qCN3/xYkXGRsA==",
10137+ "requires": {
10138+ "bindings": "^1.2.1",
10139+ "bip66": "^1.1.3",
10140+ "bn.js": "^4.11.3",
10141+ "create-hash": "^1.1.2",
10142+ "drbg.js": "^1.0.1",
10143+ "elliptic": "^6.2.3",
10144+ "nan": "^2.2.1",
10145+ "safe-buffer": "^5.1.0"
10146+ }
10147+ },
10148+ "uuid": {
10149+ "version": "3.0.0",
10150+ "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.0.0.tgz",
10151+ "integrity": "sha1-Zyj8BFnEUNeWqZwxg3VpvfZy1yg="
10152+ }
10153+ }
10154+ },
10120 "killable": {
10155 "killable": {
10121 "version": "1.0.1",
10156 "version": "1.0.1",
10122 "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
10157 "resolved": "https://registry.npmjs.org/killable/-/killable-1.0.1.tgz",
@@ -17622,6 +17657,11 @@
17622 "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz",
17657 "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-0.1.1.tgz",
17623 "integrity": "sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g=="
17658 "integrity": "sha512-PmGOd02bM9YO5ifxpw36nrNMBTptEtfRl4qUYl9SndkolplkrZZOW7PGHjrZL53QvMVj9nQ+TKqUnRsw4tJa4g=="
17624 },
17659 },
17660+ "sjcl": {
17661+ "version": "1.0.6",
17662+ "resolved": "https://registry.npmjs.org/sjcl/-/sjcl-1.0.6.tgz",
17663+ "integrity": "sha1-ZBVGKmPMDUIVxJuuydP6DBtTUg8="
17664+ },
17625 "slash": {
17665 "slash": {
17626 "version": "1.0.0",
17666 "version": "1.0.0",
17627 "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
17667 "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz",
package.json+2−1
@@ -16,7 +16,7 @@
16 "dependencies": {
16 "dependencies": {
17 "@ledgerhq/hw-app-eth": "^4.24.0",
17 "@ledgerhq/hw-app-eth": "^4.24.0",
18 "@ledgerhq/hw-transport-u2f": "^4.28.0",
18 "@ledgerhq/hw-transport-u2f": "^4.28.0",
19− "azimuth-js": "0.8.3",
19+ "azimuth-js": "^0.10.0",
20 "azimuth-solidity": "1.0.2",
20 "azimuth-solidity": "1.0.2",
21 "babel-polyfill": "^6.26.0",
21 "babel-polyfill": "^6.26.0",
22 "bip32": "^1.0.2",
22 "bip32": "^1.0.2",
@@ -29,6 +29,7 @@
29 "folktale": "^2.3.1",
29 "folktale": "^2.3.1",
30 "immutable": "^4.0.0-rc.12",
30 "immutable": "^4.0.0-rc.12",
31 "keccak": "^1.4.0",
31 "keccak": "^1.4.0",
32+ "keythereum": "^1.0.4",
32 "lodash": "^4.17.11",
33 "lodash": "^4.17.11",
33 "prop-types": "^15.6.2",
34 "prop-types": "^15.6.2",
34 "react": "^16.6.0",
35 "react": "^16.6.0",
src/Bridge.js+19−1
@@ -17,11 +17,29 @@ class Bridge extends React.Component {
17 constructor(props) {
17 constructor(props) {
18 super(props)
18 super(props)
19
19
20+ const networkType =
21+ process.env.NODE_ENV === 'development'
22+ ? NETWORK_NAMES.LOCAL
23+ : NETWORK_NAMES.MAINNET
24+
25+ // NB (jtobin):
26+ //
27+ // Note that the 'wallet' prop has type depending on the 'walletType' prop.
28+ // These could be bound together in a single structure (so that
29+ // 'walletType' acted more explicitly as a data constructor of sorts) but
30+ // it doesn't necessarily help us much, since we branch on 'walletType'
31+ // before setting 'wallet'.
32+ //
33+ // Wallets are always wrapped in Maybe. For most authentication
34+ // mechanisms, Maybe wraps a BIP32 HD wallet; for Ethereum private keys,
35+ // JSON keystore files, and Metamask authentication, it wraps an
36+ // 'EthereumWallet'.
37+
20 this.state = {
38 this.state = {
21 // routes
39 // routes
22 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
40 routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
23 // network
41 // network
24− networkType: NETWORK_NAMES.MAINNET,
42+ networkType: networkType,
25 web3: Maybe.Nothing(),
43 web3: Maybe.Nothing(),
26 contracts: Maybe.Nothing(),
44 contracts: Maybe.Nothing(),
27 // wallet
45 // wallet
src/components/Base.js+2
@@ -17,6 +17,7 @@ import {
17
17
18// Buttons
18// Buttons
19import Button from './Button';
19import Button from './Button';
20+import UploadButton from './UploadButton';
20// import Checkbox from './Checkbox';
21// import Checkbox from './Checkbox';
21import CheckboxButton from './CheckboxButton';
22import CheckboxButton from './CheckboxButton';
22import Anchor from './Anchor';
23import Anchor from './Anchor';
@@ -153,6 +154,7 @@ const ShardInput = advancedInput({
153export {
154export {
154 Button,
155 Button,
155 CheckboxButton,
156 CheckboxButton,
157+ UploadButton,
156 Anchor,
158 Anchor,
157 InputCaption,
159 InputCaption,
158
160
src/components/UploadButton.jsnew file+29
@@ -0,0 +1,29 @@
1+import React from 'react';
2+
3+class UploadButton extends React.Component {
4+ constructor(props) {
5+ super(props);
6+ this.onChange = this.onChange.bind(this);
7+ }
8+
9+ onChange() {
10+ this.props.onChange(this.input);
11+ }
12+
13+ render() {
14+ return (
15+ <label className={this.props.className}>
16+ <input
17+ className={ 'hidden h-0 w-0 abs' }
18+ ref={(ref) => { this.input = ref; }}
19+ onChange={this.onChange}
20+ disabled={this.props.disabled}
21+ type="file"
22+ />
23+ {this.props.children}
24+ </label>
25+ );
26+ }
27+}
28+
29+export default UploadButton;
src/lib/error.js+1
@@ -4,6 +4,7 @@ const BRIDGE_ERROR = {
4 MISSING_WALLET: new Error('no wallet found'),
4 MISSING_WALLET: new Error('no wallet found'),
5 MISSING_TXN: new Error('no transaction found'),
5 MISSING_TXN: new Error('no transaction found'),
6 MISSING_CONTRACTS: new Error('no contracts found'),
6 MISSING_CONTRACTS: new Error('no contracts found'),
7+ MISSING_KEYSTORE: new Error('no keystore text found'),
7 MISSING_MNEMONIC: new Error('no authentication mnemonic found'),
8 MISSING_MNEMONIC: new Error('no authentication mnemonic found'),
8 MISSING_URBIT_WALLET: new Error('no urbit wallet found'),
9 MISSING_URBIT_WALLET: new Error('no urbit wallet found'),
9 MISSING_POINT: new Error('no point found'),
10 MISSING_POINT: new Error('no point found'),
src/lib/router.js+16−2
@@ -10,6 +10,8 @@ import Network from '../views/Network'
10import Mnemonic from '../views/Mnemonic'
10import Mnemonic from '../views/Mnemonic'
11import Ledger from '../views/Ledger'
11import Ledger from '../views/Ledger'
12import Trezor from '../views/Trezor'
12import Trezor from '../views/Trezor'
13+import PrivateKey from '../views/PrivateKey'
14+import Keystore from '../views/Keystore'
13import ViewPoint from '../views/ViewPoint'
15import ViewPoint from '../views/ViewPoint'
14import SentTransaction from '../views/SentTransaction'
16import SentTransaction from '../views/SentTransaction'
15import SetKeys from '../views/SetKeys'
17import SetKeys from '../views/SetKeys'
@@ -25,7 +27,7 @@ import Ticket from '../views/Ticket'
25import Transfer from '../views/Transfer'
27import Transfer from '../views/Transfer'
26import Wallet from '../views/Wallet'
28import Wallet from '../views/Wallet'
27
29
28−import { addressFromSecp256k1Public } from './wallet'
30+import { addressFromSecp256k1Public, EthereumWallet } from './wallet'
29import { renderNetworkType } from './network'
31import { renderNetworkType } from './network'
30import { BRIDGE_ERROR } from './error'
32import { BRIDGE_ERROR } from './error'
31
33
@@ -40,6 +42,8 @@ const ROUTE_NAMES = {
40 SHARDS: Symbol('SHARDS'),
42 SHARDS: Symbol('SHARDS'),
41 LEDGER: Symbol('LEDGER'),
43 LEDGER: Symbol('LEDGER'),
42 TREZOR: Symbol('TREZOR'),
44 TREZOR: Symbol('TREZOR'),
45+ PRIVATE_KEY: Symbol('PRIVATE_KEY'),
46+ KEYSTORE: Symbol('KEYSTORE'),
43 VIEW_SHIP: Symbol('VIEW_SHIP'),
47 VIEW_SHIP: Symbol('VIEW_SHIP'),
44 SHIPS: Symbol('SHIPS'),
48 SHIPS: Symbol('SHIPS'),
45 SHIP: Symbol('SHIP'),
49 SHIP: Symbol('SHIP'),
@@ -68,6 +72,8 @@ const createRoutes = () => {
68 routes[ROUTE_NAMES.SHARDS] = Shards
72 routes[ROUTE_NAMES.SHARDS] = Shards
69 routes[ROUTE_NAMES.LEDGER] = Ledger
73 routes[ROUTE_NAMES.LEDGER] = Ledger
70 routes[ROUTE_NAMES.TREZOR] = Trezor
74 routes[ROUTE_NAMES.TREZOR] = Trezor
75+ routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey
76+ routes[ROUTE_NAMES.KEYSTORE] = Keystore
71 routes[ROUTE_NAMES.SHIPS] = Points
77 routes[ROUTE_NAMES.SHIPS] = Points
72 routes[ROUTE_NAMES.SHIP] = Point
78 routes[ROUTE_NAMES.SHIP] = Point
73 routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
79 routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
@@ -98,7 +104,9 @@ const renderRoute = (props, route) => {
98 ? wallet.matchWith({
104 ? wallet.matchWith({
99 Nothing: () => 'Wallet',
105 Nothing: () => 'Wallet',
100 Just: (wal) =>
106 Just: (wal) =>
107+ wal.value instanceof EthereumWallet
108+ ? wal.value.address
101− addressFromSecp256k1Public(wal.value.publicKey)
109+ : addressFromSecp256k1Public(wal.value.publicKey)
102 })
110 })
103
111
104 : route === ROUTE_NAMES.MNEMONIC
112 : route === ROUTE_NAMES.MNEMONIC
@@ -116,6 +124,12 @@ const renderRoute = (props, route) => {
116 : route === ROUTE_NAMES.TREZOR
124 : route === ROUTE_NAMES.TREZOR
117 ? 'Trezor'
125 ? 'Trezor'
118
126
127+ : route === ROUTE_NAMES.PRIVATE_KEY
128+ ? 'Private Key'
129+
130+ : route === ROUTE_NAMES.KEYSTORE
131+ ? 'Keystore File'
132+
119 : route === ROUTE_NAMES.SHIPS
133 : route === ROUTE_NAMES.SHIPS
120 ? 'Points'
134 ? 'Points'
121
135
src/lib/wallet.js+16−2
@@ -15,7 +15,16 @@ const WALLET_NAMES = {
15 TICKET: Symbol('TICKET'),
15 TICKET: Symbol('TICKET'),
16 SHARDS: Symbol('SHARDS'),
16 SHARDS: Symbol('SHARDS'),
17 LEDGER: Symbol('LEDGER'),
17 LEDGER: Symbol('LEDGER'),
18− TREZOR: Symbol('TREZOR')
18+ TREZOR: Symbol('TREZOR'),
19+ PRIVATE_KEY: Symbol('PRIVATE_KEY'),
20+ KEYSTORE: Symbol('KEYSTORE')
21+}
22+
23+function EthereumWallet(privateKey) {
24+ this.privateKey = privateKey
25+ this.publicKey = secp256k1.publicKeyCreate(this.privateKey)
26+ const pub = this.publicKey.toString('hex')
27+ this.address = addressFromSecp256k1Public(pub)
19}
28}
20
29
21const renderWalletType = (wallet) =>
30const renderWalletType = (wallet) =>
@@ -29,6 +38,10 @@ const renderWalletType = (wallet) =>
29 ? 'Ledger'
38 ? 'Ledger'
30 : wallet === WALLET_NAMES.TREZOR
39 : wallet === WALLET_NAMES.TREZOR
31 ? 'Trezor'
40 ? 'Trezor'
41+ : wallet === WALLET_NAMES.PRIVATE_KEY
42+ ? 'Private Key'
43+ : wallet === WALLET_NAMES.KEYSTORE
44+ ? 'Keystore File'
32 : 'Wallet'
45 : 'Wallet'
33
46
34const addressFromSecp256k1Public = pub => {
47const addressFromSecp256k1Public = pub => {
@@ -105,5 +118,6 @@ export {
105 addressFromSecp256k1Public,
118 addressFromSecp256k1Public,
106 eqAddr,
119 eqAddr,
107 walletFromMnemonic,
120 walletFromMnemonic,
108− addHexPrefix
121+ addHexPrefix,
122+ EthereumWallet
109}
123}
src/style/extend.css+4
@@ -435,3 +435,7 @@ footer,
435code {
435code {
436 padding: 2px;
436 padding: 2px;
437}
437}
438+
439+.pointer:hover {
440+ cursor: pointer;
441+}
src/views/AcceptTransfer.js+1−4
@@ -1,6 +1,6 @@
1import Maybe from 'folktale/maybe'
1import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3−import { Row, Col, H1, H3, P, Form, Warning } from '../components/Base'
3+import { Row, Col, H1, H3, P, Warning } from '../components/Base'
4import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
4import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
5import * as azimuth from 'azimuth-js'
5import * as azimuth from 'azimuth-js'
6import * as ob from 'urbit-ob'
6import * as ob from 'urbit-ob'
@@ -277,7 +277,6 @@ class AcceptTransfer extends React.Component {
277 "as. But you can transfer to any address you like."
277 "as. But you can transfer to any address you like."
278 }
278 }
279 </P>
279 </P>
280− <Form>
281 <AddressInput
280 <AddressInput
282 className='mono mt-8'
281 className='mono mt-8'
283 prop-size='lg'
282 prop-size='lg'
@@ -329,8 +328,6 @@ class AcceptTransfer extends React.Component {
329 setGasLimit={this.handleSetGasLimit}
328 setGasLimit={this.handleSetGasLimit}
330 handleSubmit={this.handleSubmit} />
329 handleSubmit={this.handleSubmit} />
331
330
332− </Form>
333−
334 {
331 {
335 Maybe.Nothing.hasInstance(state.txError)
332 Maybe.Nothing.hasInstance(state.txError)
336 ? ''
333 ? ''
src/views/CancelTransfer.js+46−48
@@ -1,6 +1,6 @@
1import Maybe from 'folktale/maybe'
1import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3−import { Row, Col, H1, P, Form, Warning, H3 } from '../components/Base'
3+import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
4import * as azimuth from 'azimuth-js'
4import * as azimuth from 'azimuth-js'
5import * as ob from 'urbit-ob'
5import * as ob from 'urbit-ob'
6
6
@@ -227,53 +227,51 @@ class CancelTransfer extends React.Component {
227 `This action will cancel the transfer to ${proxy}.`
227 `This action will cancel the transfer to ${proxy}.`
228 }
228 }
229 </P>
229 </P>
230− <Form>
231− <StatelessTransaction
230+ <StatelessTransaction
232− // Upper scope
231+ // Upper scope
233− web3={props.web3}
232+ web3={props.web3}
234− contracts={props.contracts}
233+ contracts={props.contracts}
235− wallet={props.wallet}
234+ wallet={props.wallet}
236− walletType={props.walletType}
235+ walletType={props.walletType}
237− walletHdPath={props.walletHdPath}
236+ walletHdPath={props.walletHdPath}
238− // Tx
237+ // Tx
239− txn={state.txn}
238+ txn={state.txn}
240− stx={state.stx}
239+ stx={state.stx}
241− // Tx details
240+ // Tx details
242− nonce={state.nonce}
241+ nonce={state.nonce}
243− gasPrice={state.gasPrice}
242+ gasPrice={state.gasPrice}
244− chainId={state.chainId}
243+ chainId={state.chainId}
245− gasLimit={state.gasLimit}
244+ gasLimit={state.gasLimit}
246− // Checks
245+ // Checks
247− userApproval={state.userApproval}
246+ userApproval={state.userApproval}
248− canGenerate={ canGenerate }
247+ canGenerate={ canGenerate }
249− canSign={ canSign }
248+ canSign={ canSign }
250− canApprove={ canApprove }
249+ canApprove={ canApprove }
251− canSend={ canSend }
250+ canSend={ canSend }
252− // Methods
251+ // Methods
253− createUnsignedTxn={this.handleCreateUnsignedTxn}
252+ createUnsignedTxn={this.handleCreateUnsignedTxn}
254− setUserApproval={this.handleSetUserApproval}
253+ setUserApproval={this.handleSetUserApproval}
255− setTxn={this.handleSetTxn}
254+ setTxn={this.handleSetTxn}
256− setStx={this.handleSetStx}
255+ setStx={this.handleSetStx}
257− setNonce={this.handleSetNonce}
256+ setNonce={this.handleSetNonce}
258− setChainId={this.handleSetChainId}
257+ setChainId={this.handleSetChainId}
259− setGasPrice={this.handleSetGasPrice}
258+ setGasPrice={this.handleSetGasPrice}
260− setGasLimit={this.handleSetGasLimit}
259+ setGasLimit={this.handleSetGasLimit}
261− handleSubmit={this.handleSubmit} />
260+ handleSubmit={this.handleSubmit} />
262−
261+
263− {
262+ {
264− Maybe.Nothing.hasInstance(state.txError)
263+ Maybe.Nothing.hasInstance(state.txError)
265− ? ''
264+ ? ''
266− : <Warning className={'mt-8'}>
265+ : <Warning className={'mt-8'}>
267− <H3 style={{marginTop: 0, paddingTop: 0}}>
266+ <H3 style={{marginTop: 0, paddingTop: 0}}>
268− {
267+ {
269− 'There was an error sending your transaction.'
268+ 'There was an error sending your transaction.'
270− }
269+ }
271− </H3>
270+ </H3>
272− { state.txError.value }
271+ { state.txError.value }
273− </Warning>
272+ </Warning>
274− }
273+ }
275−
274+
276− </Form>
277 </Col>
275 </Col>
278 </Row>
276 </Row>
279 )
277 )
src/views/CreateGalaxy.js+84−87
@@ -5,7 +5,7 @@ import * as ob from 'urbit-ob'
5
5
6import { Button, } from '../components/Base'
6import { Button, } from '../components/Base'
7import { Row, Col, H1, P, Anchor } from '../components/Base'
7import { Row, Col, H1, P, Anchor } from '../components/Base'
8−import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie, Form } from '../components/Base'
8+import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie } from '../components/Base'
9import StatelessTransaction from '../components/StatelessTransaction'
9import StatelessTransaction from '../components/StatelessTransaction'
10
10
11import { BRIDGE_ERROR } from '../lib/error'
11import { BRIDGE_ERROR } from '../lib/error'
@@ -192,93 +192,90 @@ class CreateGalaxy extends React.Component {
192 }
192 }
193 </P>
193 </P>
194
194
195− <Form>
196−
197− <GalaxyInput
195+ <GalaxyInput
198− className='mono'
196+ className='mono'
199− prop-size='lg'
197+ prop-size='lg'
200− prop-format='innerLabel'
198+ prop-format='innerLabel'
201− autoFocus
199+ autoFocus
202− placeholder='e.g. ~zod'
200+ placeholder='e.g. ~zod'
203− value={state.galaxyName}
201+ value={state.galaxyName}
204− onChange={v => this.handleGalaxyNameInput(v)}>
202+ onChange={v => this.handleGalaxyNameInput(v)}>
205− <InnerLabel>{ 'Galaxy Name' }</InnerLabel>
203+ <InnerLabel>{ 'Galaxy Name' }</InnerLabel>
206− <ValidatedSigil
204+ <ValidatedSigil
207− className='tr-0 mt-05 mr-0 abs'
205+ className='tr-0 mt-05 mr-0 abs'
208− patp={state.galaxyName}
206+ patp={state.galaxyName}
209− size={76}
207+ size={76}
210− margin={8} />
208+ margin={8} />
211− </GalaxyInput>
209+ </GalaxyInput>
212−
210+
213− <AddressInput
211+ <AddressInput
214− className='mono mt-8'
212+ className='mono mt-8'
215− prop-size='lg'
213+ prop-size='lg'
216− prop-format='innerLabel'
214+ prop-format='innerLabel'
217− value={ state.galaxyOwner }
215+ value={ state.galaxyOwner }
218− onChange={ v => this.handleAddressInput(v) }>
216+ onChange={ v => this.handleAddressInput(v) }>
219− <InnerLabel>
217+ <InnerLabel>
220− { 'Address that will own this galaxy' }
218+ { 'Address that will own this galaxy' }
221− </InnerLabel>
219+ </InnerLabel>
222− <ShowBlockie
220+ <ShowBlockie
223− className={'mt-1'}
224− address={state.galaxyOwner} />
225− </AddressInput>
226−
227− <Anchor
228 className={'mt-1'}
221 className={'mt-1'}
222+ address={state.galaxyOwner} />
223+ </AddressInput>
224+
225+ <Anchor
226+ className={'mt-1'}
229− prop-size={'sm'}
227+ prop-size={'sm'}
230− prop-disabled={!validAddress}
228+ prop-disabled={!validAddress}
231− target={'_blank'}
229+ target={'_blank'}
232− href={`https://etherscan.io/address/${state.galaxyOwner}`}>
230+ href={`https://etherscan.io/address/${state.galaxyOwner}`}>
233− {'View on Etherscan ↗'}
231+ {'View on Etherscan ↗'}
234− </Anchor>
232+ </Anchor>
235−
233+
236− <Button
234+ <Button
237− prop-size='lg wide'
235+ prop-size='lg wide'
238− className='mt-8'
236+ className='mt-8'
239− prop-color={buttonTriState(state.isAvailable)}
237+ prop-color={buttonTriState(state.isAvailable)}
240− disabled={!validGalaxy}
238+ disabled={!validGalaxy}
241− onClick={() => this.confirmAvailability()}>
239+ onClick={() => this.confirmAvailability()}>
242− {buttonTriStateText(state.isAvailable)}
240+ {buttonTriStateText(state.isAvailable)}
243− </Button>
241+ </Button>
244−
242+
245− <StatelessTransaction
243+ <StatelessTransaction
246− address={state.galaxyOwner}
244+ address={state.galaxyOwner}
247− shipName={state.galaxyName}
245+ shipName={state.galaxyName}
248− isAvailable={state.isAvailable}
246+ isAvailable={state.isAvailable}
249− web3={props.web3}
247+ web3={props.web3}
250− contracts={props.contracts}
248+ contracts={props.contracts}
251− wallet={props.wallet}
249+ wallet={props.wallet}
252− walletType={props.walletType}
250+ walletType={props.walletType}
253− walletHdPath={props.walletHdPath}
251+ walletHdPath={props.walletHdPath}
254− // Tx
252+ // Tx
255− txApproval={state.txApproval}
253+ txApproval={state.txApproval}
256− txn={state.txn}
254+ txn={state.txn}
257− stx={state.stx}
255+ stx={state.stx}
258− nonce={state.nonce}
256+ nonce={state.nonce}
259− gasPrice={state.gasPrice}
257+ gasPrice={state.gasPrice}
260− chainId={state.chainId}
258+ chainId={state.chainId}
261− gasLimit={state.gasLimit}
259+ gasLimit={state.gasLimit}
262− canGenerate={
260+ canGenerate={
263− validAddress === true &&
261+ validAddress === true &&
264− validGalaxy === true &&
262+ validGalaxy === true &&
265− state.isAvailable === true
263+ state.isAvailable === true
266− }
264+ }
267− canSign={!Maybe.Nothing.hasInstance(state.txn)}
265+ canSign={!Maybe.Nothing.hasInstance(state.txn)}
268− canSend={!Maybe.Nothing.hasInstance(state.stx)}
266+ canSend={!Maybe.Nothing.hasInstance(state.stx)}
269− // Methods
267+ // Methods
270− createUnsignedTxn={() => this.createUnsignedTxn()}
268+ createUnsignedTxn={() => this.createUnsignedTxn()}
271− clearTransaction={() => this.clearTransaction()}
269+ clearTransaction={() => this.clearTransaction()}
272− setApproval={() => this.setState({txApproval: !state.txApproval})}
270+ setApproval={() => this.setState({txApproval: !state.txApproval})}
273− setTxn={txn => this.setState({txn})}
271+ setTxn={txn => this.setState({txn})}
274− setStx={stx => this.setState({stx})}
272+ setStx={stx => this.setState({stx})}
275− setNonce={nonce => this.setState({nonce})}
273+ setNonce={nonce => this.setState({nonce})}
276− setChainId={chainId => this.setState({chainId})}
274+ setChainId={chainId => this.setState({chainId})}
277− setGasPrice={gasPrice => this.setState({gasPrice})}
275+ setGasPrice={gasPrice => this.setState({gasPrice})}
278− setGasLimit={gasLimit => this.setState({gasLimit})}
276+ setGasLimit={gasLimit => this.setState({gasLimit})}
279− handleSubmit={this.handleSubmit} />
277+ handleSubmit={this.handleSubmit} />
280−
278+
281− </Form>
282 </Col>
279 </Col>
283 </Row>
280 </Row>
284 )
281 )
src/views/GenKeyfile.js+23−25
@@ -3,7 +3,7 @@ import React from 'react'
3import { Button } from '../components/Base'
3import { Button } from '../components/Base'
4import { RequiredInput, InnerLabel } from '../components/Base'
4import { RequiredInput, InnerLabel } from '../components/Base'
5import { Row, Col, H1, P } from '../components/Base'
5import { Row, Col, H1, P } from '../components/Base'
6−import { Form, Code } from '../components/Base'
6+import { Code } from '../components/Base'
7
7
8import * as ob from 'urbit-ob'
8import * as ob from 'urbit-ob'
9import * as kg from '../../node_modules/urbit-key-generation/dist/index'
9import * as kg from '../../node_modules/urbit-key-generation/dist/index'
@@ -124,32 +124,30 @@ class GenKeyfile extends React.Component {
124 }
124 }
125 </P>
125 </P>
126
126
127− <Form>
128− <RequiredInput
127+ <RequiredInput
129− className='mono'
128+ className='mono'
130− prop-size='lg'
129+ prop-size='lg'
131− prop-format='innerLabel'
130+ prop-format='innerLabel'
132− autoFocus
131+ autoFocus
133− value={ networkSeed }
132+ value={ networkSeed }
134− onChange={ this.handleNetworkSeedInput }>
133+ onChange={ this.handleNetworkSeedInput }>
135− <InnerLabel>{ 'Network seed' }</InnerLabel>
134+ <InnerLabel>{ 'Network seed' }</InnerLabel>
136− </RequiredInput>
135+ </RequiredInput>
137−
136+
138− <Button
137+ <Button
139− className={'mt-8'}
138+ className={'mt-8'}
140− color={'blue'}
139+ color={'blue'}
141− onClick={
140+ onClick={
142− () => {
141+ () => {
143− if (hexRegExp.test(networkSeed)) {
142+ if (hexRegExp.test(networkSeed)) {
144− const keyfile = genKey(networkSeed, point, revision)
143+ const keyfile = genKey(networkSeed, point, revision)
145− this.setState({ keyfile })
144+ this.setState({ keyfile })
146− }
147 }
145 }
148 }
146 }
147+ }
149− >
148+ >
150− { 'Generate →' }
149+ { 'Generate →' }
151− </Button>
150+ </Button>
152− </Form>
153
151
154 <Code>
152 <Code>
155 { keyfile }
153 { keyfile }
src/views/IssueChild.js+82−85
@@ -5,7 +5,7 @@ import * as ob from 'urbit-ob'
5
5
6import { Row, Col, H1, H3, P, Anchor, Warning } from '../components/Base'
6import { Row, Col, H1, H3, P, Anchor, Warning } from '../components/Base'
7import { Button, ShowBlockie, ValidatedSigil } from '../components/Base'
7import { Button, ShowBlockie, ValidatedSigil } from '../components/Base'
8−import { PointInput, AddressInput, Form, InnerLabel } from '../components/Base'
8+import { PointInput, AddressInput, InnerLabel } from '../components/Base'
9
9
10import StatelessTransaction from '../components/StatelessTransaction'
10import StatelessTransaction from '../components/StatelessTransaction'
11
11
@@ -402,92 +402,89 @@ class IssueChild extends React.Component {
402 { '.' }
402 { '.' }
403 </P>
403 </P>
404
404
405− <Form>
406− <PointInput
405+ <PointInput
407− prop-size='lg'
406+ prop-size='lg'
408− prop-format='innerLabel'
407+ prop-format='innerLabel'
409− className={'mono mt-8'}
408+ className={'mono mt-8'}
410− placeholder={ `e.g. ${state.suggestions[3]}` }
409+ placeholder={ `e.g. ${state.suggestions[3]}` }
411− value={ state.desiredPoint }
410+ value={ state.desiredPoint }
412− onChange={ this.handlePointInput }>
411+ onChange={ this.handlePointInput }>
413− <InnerLabel>{ 'Point to Issue' }</InnerLabel>
412+ <InnerLabel>{ 'Point to Issue' }</InnerLabel>
414− <ValidatedSigil
413+ <ValidatedSigil
415− className={'tr-0 mt-05 mr-0 abs'}
414+ className={'tr-0 mt-05 mr-0 abs'}
416− patp={state.desiredPoint}
415+ patp={state.desiredPoint}
417− size={76}
416+ size={76}
418− margin={8}
417+ margin={8}
419− validator={() => this.validatePoint(state.desiredPoint)} />
418+ validator={() => this.validatePoint(state.desiredPoint)} />
420− </PointInput>
419+ </PointInput>
421−
420+
422− <AddressInput
421+ <AddressInput
423− className='text-mono mt-8'
422+ className='text-mono mt-8'
424− prop-size='lg'
423+ prop-size='lg'
425− prop-format='innerLabel'
424+ prop-format='innerLabel'
426− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
425+ placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
427− value={ state.receivingAddress }
426+ value={ state.receivingAddress }
428− disabled={ !state.available }
427+ disabled={ !state.available }
429− onChange={ v => this.handleAddressInput(v) }>
428+ onChange={ v => this.handleAddressInput(v) }>
430− <InnerLabel>{ 'Receiving Address' }</InnerLabel>
429+ <InnerLabel>{ 'Receiving Address' }</InnerLabel>
431− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
430+ <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
432− </AddressInput>
431+ </AddressInput>
433−
432+
434− <Anchor
433+ <Anchor
435− className={'mt-1'}
434+ className={'mt-1'}
436− prop-size={'sm'}
435+ prop-size={'sm'}
437− prop-disabled={!isValidAddress(state.receivingAddress)}
436+ prop-disabled={!isValidAddress(state.receivingAddress)}
438− target={'_blank'}
437+ target={'_blank'}
439− href={`https://etherscan.io/address/${state.receivingAddress}`}>
438+ href={`https://etherscan.io/address/${state.receivingAddress}`}>
440− {'View on Etherscan ↗'}
439+ {'View on Etherscan ↗'}
441− </Anchor>
440+ </Anchor>
442
441
443− {
442+ {
444− props.web3.matchWith({
443+ props.web3.matchWith({
445− Nothing: _ => <div />,
444+ Nothing: _ => <div />,
446− Just: _ =>
445+ Just: _ =>
447− <Button
446+ <Button
448− prop-size='lg wide'
447+ prop-size='lg wide'
449− prop-color={this.buttonTriState()}
448+ prop-color={this.buttonTriState()}
450− className={'mt-8'}
449+ className={'mt-8'}
451− disabled={!validPoint}
450+ disabled={!validPoint}
452− onClick={this.handleConfirmAvailability}>
451+ onClick={this.handleConfirmAvailability}>
453− {this.buttonTriStateText()}
452+ {this.buttonTriStateText()}
454− </Button>
453+ </Button>
455− })
454+ })
456− }
455+ }
457
456
458− <StatelessTransaction
457+ <StatelessTransaction
459− // Upper scope
458+ // Upper scope
460− web3={props.web3}
459+ web3={props.web3}
461− contracts={props.contracts}
460+ contracts={props.contracts}
462− wallet={props.wallet}
461+ wallet={props.wallet}
463− walletType={props.walletType}
462+ walletType={props.walletType}
464− walletHdPath={props.walletHdPath}
463+ walletHdPath={props.walletHdPath}
465− // Tx
464+ // Tx
466− txn={state.txn}
465+ txn={state.txn}
467− stx={state.stx}
466+ stx={state.stx}
468− // Tx details
467+ // Tx details
469− nonce={state.nonce}
468+ nonce={state.nonce}
470− gasPrice={state.gasPrice}
469+ gasPrice={state.gasPrice}
471− chainId={state.chainId}
470+ chainId={state.chainId}
472− gasLimit={state.gasLimit}
471+ gasLimit={state.gasLimit}
473− // Checks
472+ // Checks
474− userApproval={state.userApproval}
473+ userApproval={state.userApproval}
475− canGenerate={ canGenerate }
474+ canGenerate={ canGenerate }
476− canSign={ canSign }
475+ canSign={ canSign }
477− canApprove={ canApprove }
476+ canApprove={ canApprove }
478− canSend={ canSend }
477+ canSend={ canSend }
479− // Methods
478+ // Methods
480− createUnsignedTxn={this.handleCreateUnsignedTxn}
479+ createUnsignedTxn={this.handleCreateUnsignedTxn}
481− setUserApproval={this.handleSetUserApproval}
480+ setUserApproval={this.handleSetUserApproval}
482− setTxn={this.handleSetTxn}
481+ setTxn={this.handleSetTxn}
483− setStx={this.handleSetStx}
482+ setStx={this.handleSetStx}
484− setNonce={this.handleSetNonce}
483+ setNonce={this.handleSetNonce}
485− setChainId={this.handleSetChainId}
484+ setChainId={this.handleSetChainId}
486− setGasPrice={this.handleSetGasPrice}
485+ setGasPrice={this.handleSetGasPrice}
487− setGasLimit={this.handleSetGasLimit}
486+ setGasLimit={this.handleSetGasLimit}
488− handleSubmit={this.handleSubmit} />
487+ handleSubmit={this.handleSubmit} />
489−
490− </Form>
491
488
492 {
489 {
493 Maybe.Nothing.hasInstance(state.txError)
490 Maybe.Nothing.hasInstance(state.txError)
src/views/Keystore.jsnew file+168
@@ -0,0 +1,168 @@
1+import { Just, Nothing } from 'folktale/maybe'
2+import { Ok, Error } from 'folktale/result'
3+import React from 'react'
4+import * as keythereum from 'keythereum'
5+import { Button, UploadButton } from '../components/Base'
6+import { Input, InnerLabel, InputCaption } from '../components/Base'
7+import { Row, Col, H1, H3, Warning } from '../components/Base'
8+
9+import { BRIDGE_ERROR } from '../lib/error'
10+import { ROUTE_NAMES } from '../lib/router'
11+import { EthereumWallet } from '../lib/wallet'
12+
13+class Keystore extends React.Component {
14+
15+ constructor(props) {
16+ super(props)
17+
18+ this.state = {
19+ keystore: Nothing(), // Maybe<Result<String, String>>
20+ password: '',
21+ decryptionProblem: false
22+ }
23+
24+ this.handleKeystoreUpload = this.handleKeystoreUpload.bind(this)
25+ this.handlePasswordInput = this.handlePasswordInput.bind(this)
26+ this.constructWallet = this.constructWallet.bind(this)
27+ }
28+
29+ handlePasswordInput(password) {
30+ this.setState({ password })
31+ }
32+
33+ constructWallet() {
34+ const { setWallet } = this.props
35+ const { keystore, password } = this.state
36+
37+ try {
38+
39+ const text = keystore.matchWith({
40+ Nothing: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE },
41+ Just: ks => ks.value.matchWith({
42+ Ok: result => result.value,
43+ Error: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE }
44+ })
45+ })
46+
47+ const json = JSON.parse(text)
48+ const privateKey = keythereum.recover(password, json)
49+
50+ const wallet = new EthereumWallet(privateKey)
51+ this.setState({ decryptionProblem: false })
52+ setWallet(Just(wallet))
53+
54+ } catch (err) {
55+
56+ this.setState({ decryptionProblem: true })
57+ setWallet(Nothing())
58+
59+ }
60+ }
61+
62+ handleKeystoreUpload = (event) => {
63+ const file = event.files.item(0)
64+ const reader = new FileReader()
65+
66+ reader.onload = (e) => {
67+ const keystore = e.target.result
68+ this.setState({ keystore: Just(Ok(keystore)) })
69+ }
70+
71+ const failure = _ => {
72+ const message = 'There was a problem uploading your Keystore file'
73+ this.setState({ keystore: Just(Error(message)) })
74+ }
75+
76+ reader.onerror = failure
77+ reader.onabort = failure
78+
79+ reader.readAsText(file)
80+ }
81+
82+ render() {
83+ const { pushRoute, popRoute, wallet } = this.props
84+ const { keystore, password, decryptionProblem } = this.state
85+
86+ const uploadButtonClass = keystore.matchWith({
87+ Nothing: _ => 'bg-blue white',
88+ Just: ks => ks.value.matchWith({
89+ Ok: _ => 'bg-green white',
90+ Error: _ => 'bg-yellow black'
91+ })
92+ })
93+
94+ const decryptMessage =
95+ decryptionProblem === false
96+ ? <div />
97+ : <Warning className={'mt-8'}>
98+ <H3 style={{marginTop: 0, paddingTop: 0}}>
99+ {
100+ "Couldn't decrypt wallet."
101+ }
102+ </H3>
103+ { 'You may have entered an incorrect password.' }
104+ </Warning>
105+
106+ return (
107+ <Row>
108+ <Col className={'measure-md'}>
109+ <H1 className={'mb-4'}>{ 'Upload Your Keystore File' }</H1>
110+ <InputCaption>
111+ { `Please upload your Ethereum keystore file. If your keystore
112+ file is encrypted with a password, you'll also need to enter
113+ that below.` }
114+ </InputCaption>
115+
116+ <UploadButton
117+ className={ `btn ${uploadButtonClass} mt-10` }
118+ onChange={ this.handleKeystoreUpload }
119+ >
120+ <div className={'flex-center-all fs-4 h-11 pointer'}>{ 'Upload Keystore file' }</div>
121+ </UploadButton>
122+
123+ <Input
124+ className='pt-8 mt-8'
125+ prop-size='md'
126+ prop-format='innerLabel'
127+ type='password'
128+ name='password'
129+ onChange={ this.handlePasswordInput }
130+ value={ password }
131+ autocomplete='off'
132+ autoFocus>
133+ <InnerLabel>{'Password'}</InnerLabel>
134+ </Input>
135+
136+ <Button
137+ className={` mt-10`}
138+ prop-size={ 'wide lg' }
139+ disabled={ Nothing.hasInstance(keystore) }
140+ onClick={ this.constructWallet }
141+ >
142+ { 'Decrypt' }
143+ </Button>
144+
145+ { decryptMessage }
146+
147+ <Button
148+ className={'mt-10'}
149+ prop-size={ 'wide lg' }
150+ disabled={ Nothing.hasInstance(wallet) }
151+ onClick={ () => {
152+ popRoute()
153+ pushRoute(ROUTE_NAMES.SHIPS)
154+ }
155+ }
156+ >
157+ { 'Continue →' }
158+ </Button>
159+
160+ </Col>
161+ </Row>
162+
163+
164+ )
165+ }
166+}
167+
168+export default Keystore
src/views/Ledger.js+31−33
@@ -3,7 +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'
6+import { Input, InnerLabel } from '../components/Base'
7import Transport from '@ledgerhq/hw-transport-u2f'
7import Transport from '@ledgerhq/hw-transport-u2f'
8import Eth from '@ledgerhq/hw-app-eth'
8import Eth from '@ledgerhq/hw-app-eth'
9import * as secp256k1 from 'secp256k1'
9import * as secp256k1 from 'secp256k1'
@@ -99,38 +99,36 @@ class Ledger extends React.Component {
99 <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>
100 </ol>
100 </ol>
101
101
102− <Form>
103− <Input
102+ <Input
104− className='pt-8 mt-8'
103+ className='pt-8 mt-8'
105− prop-size='md'
104+ prop-size='md'
106− prop-format='innerLabel'
105+ prop-format='innerLabel'
107− name='hdpath'
106+ name='hdpath'
108− value={ addHdPrefix(hdpath) }
107+ value={ addHdPrefix(hdpath) }
109− autocomplete='off'
108+ autocomplete='off'
110− onChange={ this.handleHdPathInput }>
109+ onChange={ this.handleHdPathInput }>
111− <InnerLabel>{'HD Path'}</InnerLabel>
110+ <InnerLabel>{'HD Path'}</InnerLabel>
112− </Input>
111+ </Input>
113−
112+
114− <Button
113+ <Button
115− prop-size={'wide lg'}
114+ prop-size={'wide lg'}
116− className={'mt-8'}
115+ className={'mt-8'}
117− onClick={ this.pollDevice }>
116+ onClick={ this.pollDevice }>
118− { 'Authenticate →' }
117+ { 'Authenticate →' }
119− </Button>
118+ </Button>
120−
119+
121− <Button
120+ <Button
122− className={'mt-8'}
121+ className={'mt-8'}
123− prop-size={'wide lg'}
122+ prop-size={'wide lg'}
124− disabled={ Maybe.Nothing.hasInstance(wallet) }
123+ disabled={ Maybe.Nothing.hasInstance(wallet) }
125− onClick={
124+ onClick={
126− () => {
125+ () => {
127− popRoute()
126+ popRoute()
128− pushRoute(ROUTE_NAMES.SHIPS)
127+ pushRoute(ROUTE_NAMES.SHIPS)
129− }
128+ }
130− }>
129+ }>
131− { 'Continue →' }
130+ { 'Continue →' }
132− </Button>
131+ </Button>
133− </Form>
134
132
135 </Col>
133 </Col>
136 </Row>
134 </Row>
src/views/Mnemonic.js+1−3
@@ -3,7 +3,7 @@ import Maybe from 'folktale/maybe'
3import React from 'react'
3import React from 'react'
4import { Button } from '../components/Base'
4import { Button } from '../components/Base'
5import { Input, MnemonicInput, InnerLabel, InputCaption } from '../components/Base'
5import { Input, MnemonicInput, InnerLabel, InputCaption } from '../components/Base'
6−import { Row, Col, H1, Form } from '../components/Base'
6+import { Row, Col, H1 } from '../components/Base'
7
7
8import { ROUTE_NAMES } from '../lib/router'
8import { ROUTE_NAMES } from '../lib/router'
9import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet'
9import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet'
@@ -78,7 +78,6 @@ class Mnemonic extends React.Component {
78 <Row>
78 <Row>
79 <Col>
79 <Col>
80 <H1 className={'mb-4'}>{ 'Enter Your Mnemonic' }</H1>
80 <H1 className={'mb-4'}>{ 'Enter Your Mnemonic' }</H1>
81− <Form>
82 <InputCaption>
81 <InputCaption>
83 { "Please enter your BIP39 mnemonic here." }
82 { "Please enter your BIP39 mnemonic here." }
84 </InputCaption>
83 </InputCaption>
@@ -124,7 +123,6 @@ class Mnemonic extends React.Component {
124 >
123 >
125 { 'Continue →' }
124 { 'Continue →' }
126 </Button>
125 </Button>
127− </Form>
128 </Col>
126 </Col>
129 </Row>
127 </Row>
130
128
src/views/PrivateKey.jsnew file+85
@@ -0,0 +1,85 @@
1+import Maybe from 'folktale/maybe'
2+import React from 'react'
3+import { Button } from '../components/Base'
4+import { RequiredInput, InnerLabel, InputCaption } from '../components/Base'
5+import { Row, Col, H1 } from '../components/Base'
6+
7+import { ROUTE_NAMES } from '../lib/router'
8+import { EthereumWallet } from '../lib/wallet'
9+
10+const secre = /[0-9A-Fa-f]{64}/g
11+
12+class PrivateKey extends React.Component {
13+
14+ constructor(props) {
15+ super(props)
16+
17+ this.state = {
18+ privateKey: '',
19+ }
20+
21+ this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this)
22+ }
23+
24+ handlePrivateKeyInput(privateKey) {
25+ this.setState({ privateKey })
26+ this.constructWallet(privateKey)
27+ }
28+
29+ constructWallet(privateKey) {
30+ const { setWallet } = this.props
31+ if (secre.test(privateKey) === true) {
32+ const sec = Buffer.from(privateKey, 'hex')
33+ const wallet = new EthereumWallet(sec)
34+ setWallet(Maybe.Just(wallet))
35+ } else {
36+ setWallet(Maybe.Nothing())
37+ }
38+ }
39+
40+ render() {
41+ const { pushRoute, popRoute, wallet } = this.props
42+ const { privateKey } = this.state
43+
44+ return (
45+ <Row>
46+ <Col className={'measure-md'}>
47+ <H1 className={'mb-4'}>{ 'Enter Your Private Key' }</H1>
48+ <InputCaption>
49+ { `Please enter your raw Ethereum private key here.` }
50+ </InputCaption>
51+
52+ <RequiredInput
53+ className='pt-8 mt-8'
54+ prop-size='md'
55+ prop-format='innerLabel'
56+ type='password'
57+ name='privateKey'
58+ onChange={ this.handlePrivateKeyInput }
59+ value={ privateKey }
60+ autocomplete='off'
61+ autoFocus>
62+ <InnerLabel>{'Private Key'}</InnerLabel>
63+ </RequiredInput>
64+
65+ <Button
66+ className={'mt-10'}
67+ prop-size={ 'wide lg' }
68+ disabled={ Maybe.Nothing.hasInstance(wallet) }
69+ onClick={ () => {
70+ popRoute()
71+ pushRoute(ROUTE_NAMES.SHIPS)
72+ }
73+ }
74+ >
75+ { 'Continue →' }
76+ </Button>
77+ </Col>
78+ </Row>
79+
80+
81+ )
82+ }
83+}
84+
85+export default PrivateKey
src/views/SetKeys.js+54−58
@@ -2,7 +2,7 @@ import React from 'react'
2import Maybe from 'folktale/maybe'
2import Maybe from 'folktale/maybe'
3import * as azimuth from 'azimuth-js'
3import * as azimuth from 'azimuth-js'
4import * as ob from 'urbit-ob'
4import * as ob from 'urbit-ob'
5−import { Row, Col, H1, P, Warning, Form, H3 } from '../components/Base'
5+import { Row, Col, H1, P, Warning, H3 } from '../components/Base'
6import { RequiredInput, InnerLabel } from '../components/Base'
6import { RequiredInput, InnerLabel } from '../components/Base'
7
7
8import StatelessTransaction from '../components/StatelessTransaction'
8import StatelessTransaction from '../components/StatelessTransaction'
@@ -313,63 +313,59 @@ class SetKeys extends React.Component {
313 </Warning>
313 </Warning>
314 : <div /> }
314 : <div /> }
315
315
316− <Form>
317− <RequiredInput
316+ <RequiredInput
318− className='mono'
317+ className='mono'
319− prop-size='lg'
318+ prop-size='lg'
320− prop-format='innerLabel'
319+ prop-format='innerLabel'
321− autoFocus
320+ autoFocus
322− value={ state.networkSeed }
321+ value={ state.networkSeed }
323− onChange={ this.handleNetworkSeedInput }>
322+ onChange={ this.handleNetworkSeedInput }>
324− <InnerLabel>{ 'Network seed' }</InnerLabel>
323+ <InnerLabel>{ 'Network seed' }</InnerLabel>
325− </RequiredInput>
324+ </RequiredInput>
326−
325+
327− <StatelessTransaction
326+ <StatelessTransaction
328− // Upper scope
327+ // Upper scope
329− web3={props.web3}
328+ web3={props.web3}
330− contracts={props.contracts}
329+ contracts={props.contracts}
331− wallet={props.wallet}
330+ wallet={props.wallet}
332− walletType={props.walletType}
331+ walletType={props.walletType}
333− walletHdPath={props.walletHdPath}
334− // Tx
332+ // Tx
335− txn={state.txn}
333+ txn={state.txn}
336− stx={state.stx}
334+ stx={state.stx}
337− // Tx details
335+ // Tx details
338− nonce={state.nonce}
336+ nonce={state.nonce}
339− gasPrice={state.gasPrice}
337+ gasPrice={state.gasPrice}
340− chainId={state.chainId}
338+ chainId={state.chainId}
341− gasLimit={state.gasLimit}
339+ gasLimit={state.gasLimit}
342− // Checks
340+ // Checks
343− userApproval={state.userApproval}
341+ userApproval={state.userApproval}
344− canGenerate={ canGenerate }
342+ canGenerate={ canGenerate }
345− canSign={ canSign }
343+ canSign={ canSign }
346− canApprove={ canApprove }
344+ canApprove={ canApprove }
347− canSend={ canSend }
345+ canSend={ canSend }
348− // Methods
346+ // Methods
349− createUnsignedTxn={this.handleCreateUnsignedTxn}
347+ createUnsignedTxn={this.handleCreateUnsignedTxn}
350− setUserApproval={this.handleSetUserApproval}
348+ setUserApproval={this.handleSetUserApproval}
351− setTxn={this.handleSetTxn}
349+ setTxn={this.handleSetTxn}
352− setStx={this.handleSetStx}
350+ setStx={this.handleSetStx}
353− setNonce={this.handleSetNonce}
351+ setNonce={this.handleSetNonce}
354− setChainId={this.handleSetChainId}
352+ setChainId={this.handleSetChainId}
355− setGasPrice={this.handleSetGasPrice}
353+ setGasPrice={this.handleSetGasPrice}
356− setGasLimit={this.handleSetGasLimit}
354+ setGasLimit={this.handleSetGasLimit}
357− handleSubmit={this.handleSubmit} />
355+ handleSubmit={this.handleSubmit} />
358−
356+
359− {
357+ {
360− Maybe.Nothing.hasInstance(state.txError)
358+ Maybe.Nothing.hasInstance(state.txError)
361− ? ''
359+ ? ''
362− : <Warning className={'mt-8'}>
360+ : <Warning className={'mt-8'}>
363− <H3 style={{marginTop: 0, paddingTop: 0}}>
361+ <H3 style={{marginTop: 0, paddingTop: 0}}>
364− {
362+ {
365− 'There was an error sending your transaction.'
363+ 'There was an error sending your transaction.'
366− }
364+ }
367− </H3>
365+ </H3>
368− { state.txError.value }
366+ { state.txError.value }
369− </Warning>
367+ </Warning>
370− }
368+ }
371−
372− </Form>
373
369
374 </Col>
370 </Col>
375 </Row>
371 </Row>
src/views/SetProxy.js+66−68
@@ -2,7 +2,7 @@ import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3import * as azimuth from 'azimuth-js'
3import * as azimuth from 'azimuth-js'
4import { Row, Col, H1, H3, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
4import { Row, Col, H1, H3, P, InnerLabel, ShowBlockie, Anchor } from '../components/Base'
5−import { AddressInput, Form, Warning } from '../components/Base'
5+import { AddressInput, Warning } from '../components/Base'
6import * as ob from 'urbit-ob'
6import * as ob from 'urbit-ob'
7
7
8import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
8import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
@@ -293,73 +293,71 @@ class SetProxy extends React.Component {
293 'Generator to generate a keypair.'
293 'Generator to generate a keypair.'
294 }
294 }
295 </P>
295 </P>
296− <Form>
297− <AddressInput
296+ <AddressInput
298− className='mono mt-8'
297+ className='mono mt-8'
299− prop-size='lg'
298+ prop-size='lg'
300− prop-format='innerLabel'
299+ prop-format='innerLabel'
301− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
300+ placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
302− value={ state.proxyAddress }
301+ value={ state.proxyAddress }
303− onChange={ v => this.handleAddressInput(v) }>
302+ onChange={ v => this.handleAddressInput(v) }>
304− <InnerLabel>{ 'Proxy Address' }</InnerLabel>
303+ <InnerLabel>{ 'Proxy Address' }</InnerLabel>
305− <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
304+ <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
306− </AddressInput>
305+ </AddressInput>
307−
306+
308− <Anchor
307+ <Anchor
309− className={'mt-1'}
308+ className={'mt-1'}
310− prop-size={'sm'}
309+ prop-size={'sm'}
311− disabled={!isValidAddress(state.proxyAddress)}
310+ disabled={!isValidAddress(state.proxyAddress)}
312− target={'_blank'}
311+ target={'_blank'}
313− href={`https://etherscan.io/address/${state.proxyAddress}`}>
312+ href={`https://etherscan.io/address/${state.proxyAddress}`}>
314− {'View on Etherscan ↗'}
313+ {'View on Etherscan ↗'}
315− </Anchor>
314+ </Anchor>
316−
315+
317− <StatelessTransaction
316+ <StatelessTransaction
318− // Upper scope
317+ // Upper scope
319− web3={props.web3}
318+ web3={props.web3}
320− contracts={props.contracts}
319+ contracts={props.contracts}
321− wallet={props.wallet}
320+ wallet={props.wallet}
322− walletType={props.walletType}
321+ walletType={props.walletType}
323− walletHdPath={props.walletHdPath}
322+ walletHdPath={props.walletHdPath}
324− // Tx
323+ // Tx
325− txn={state.txn}
324+ txn={state.txn}
326− stx={state.stx}
325+ stx={state.stx}
327− // Tx details
326+ // Tx details
328− nonce={state.nonce}
327+ nonce={state.nonce}
329− gasPrice={state.gasPrice}
328+ gasPrice={state.gasPrice}
330− chainId={state.chainId}
329+ chainId={state.chainId}
331− gasLimit={state.gasLimit}
330+ gasLimit={state.gasLimit}
332− // Checks
331+ // Checks
333− userApproval={state.userApproval}
332+ userApproval={state.userApproval}
334− canGenerate={ canGenerate }
333+ canGenerate={ canGenerate }
335− canSign={ canSign }
334+ canSign={ canSign }
336− canApprove={ canApprove }
335+ canApprove={ canApprove }
337− canSend={ canSend }
336+ canSend={ canSend }
338− // Methods
337+ // Methods
339− createUnsignedTxn={this.handleCreateUnsignedTxn}
338+ createUnsignedTxn={this.handleCreateUnsignedTxn}
340− setUserApproval={this.handleSetUserApproval}
339+ setUserApproval={this.handleSetUserApproval}
341− setTxn={this.handleSetTxn}
340+ setTxn={this.handleSetTxn}
342− setStx={this.handleSetStx}
341+ setStx={this.handleSetStx}
343− setNonce={this.handleSetNonce}
342+ setNonce={this.handleSetNonce}
344− setChainId={this.handleSetChainId}
343+ setChainId={this.handleSetChainId}
345− setGasPrice={this.handleSetGasPrice}
344+ setGasPrice={this.handleSetGasPrice}
346− setGasLimit={this.handleSetGasLimit}
345+ setGasLimit={this.handleSetGasLimit}
347− handleSubmit={this.handleSubmit} />
346+ handleSubmit={this.handleSubmit} />
348−
347+
349− {
348+ {
350− Maybe.Nothing.hasInstance(state.txError)
349+ Maybe.Nothing.hasInstance(state.txError)
351− ? ''
350+ ? ''
352− : <Warning className={'mt-8'}>
351+ : <Warning className={'mt-8'}>
353− <H3 style={{marginTop: 0, paddingTop: 0}}>
352+ <H3 style={{marginTop: 0, paddingTop: 0}}>
354− {
353+ {
355− 'There was an error sending your transaction.'
354+ 'There was an error sending your transaction.'
356− }
355+ }
357− </H3>
356+ </H3>
358− { state.txError.value }
357+ { state.txError.value }
359− </Warning>
358+ </Warning>
360− }
359+ }
361−
360+
362− </Form>
363 </Col>
361 </Col>
364 </Row>
362 </Row>
365 )
363 )
src/views/Shards.js+77−80
@@ -2,7 +2,7 @@ import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3import { Button } from '../components/Base'
3import { Button } from '../components/Base'
4import { InnerLabel, ValidatedSigil, PointInput, ShardInput } from '../components/Base'
4import { InnerLabel, ValidatedSigil, PointInput, ShardInput } from '../components/Base'
5−import { Row, Col, H1, P, Form } from '../components/Base'
5+import { Row, Col, H1, P } from '../components/Base'
6import * as kg from '../../node_modules/urbit-key-generation/dist/index'
6import * as kg from '../../node_modules/urbit-key-generation/dist/index'
7import * as ob from 'urbit-ob'
7import * as ob from 'urbit-ob'
8
8
@@ -115,87 +115,84 @@ class Shards extends React.Component {
115
115
116 </P>
116 </P>
117
117
118− <Form>
118+ <PointInput
119−
120− <PointInput
121− className='mono mt-8'
119+ className='mono mt-8'
122− prop-size='lg'
120+ prop-size='lg'
123− prop-format='innerLabel'
121+ prop-format='innerLabel'
124− type='text'
122+ type='text'
125− autoFocus
123+ autoFocus
126− placeholder={ `e.g. ${phPoint}` }
124+ placeholder={ `e.g. ${phPoint}` }
127− value={ pointName }
125+ value={ pointName }
128− onChange={ this.handlePointNameInput }>
126+ onChange={ this.handlePointNameInput }>
129− <InnerLabel>{ 'Point' }</InnerLabel>
127+ <InnerLabel>{ 'Point' }</InnerLabel>
130− <ValidatedSigil
128+ <ValidatedSigil
131− className={'tr-0 mt-05 mr-0 abs'}
129+ className={'tr-0 mt-05 mr-0 abs'}
132− patp={pointName}
130+ patp={pointName}
133− size={68}
131+ size={68}
134− margin={8} />
132+ margin={8} />
135− </PointInput>
133+ </PointInput>
136−
134+
137−
135+
138− <ShardInput
136+ <ShardInput
139− className='mono mt-8'
137+ className='mono mt-8'
140− prop-size='md'
138+ prop-size='md'
141− prop-format='innerLabel'
139+ prop-format='innerLabel'
142− type='text'
140+ type='text'
143− name='shard1'
141+ name='shard1'
144− placeholder={ `e.g. ${phTick}` }
142+ placeholder={ `e.g. ${phTick}` }
145− value={ shard1 }
143+ value={ shard1 }
146− onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
144+ onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
147− <InnerLabel>{ 'Shard 1' }</InnerLabel>
145+ <InnerLabel>{ 'Shard 1' }</InnerLabel>
148− </ShardInput>
146+ </ShardInput>
149−
147+
150− <ShardInput
148+ <ShardInput
151− className='mono mt-8'
149+ className='mono mt-8'
152− prop-size='md'
150+ prop-size='md'
153− prop-format='innerLabel'
151+ prop-format='innerLabel'
154− type='text'
152+ type='text'
155− name='shard2'
153+ name='shard2'
156− placeholder={ `e.g. ${phTick}` }
154+ placeholder={ `e.g. ${phTick}` }
157− value={ shard2 }
155+ value={ shard2 }
158− onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
156+ onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
159− <InnerLabel>{ 'Shard 2' }</InnerLabel>
157+ <InnerLabel>{ 'Shard 2' }</InnerLabel>
160− </ShardInput>
158+ </ShardInput>
161−
159+
162− <ShardInput
160+ <ShardInput
163− className='mono mt-8'
161+ className='mono mt-8'
164− prop-size='md'
162+ prop-size='md'
165− prop-format='innerLabel'
163+ prop-format='innerLabel'
166− type='text'
164+ type='text'
167− name='shard3'
165+ name='shard3'
168− placeholder={ `e.g. ${phTick}` }
166+ placeholder={ `e.g. ${phTick}` }
169− value={ shard3 }
167+ value={ shard3 }
170− onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
168+ onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
171− <InnerLabel>{ 'Shard 3' }</InnerLabel>
169+ <InnerLabel>{ 'Shard 3' }</InnerLabel>
172− </ShardInput>
170+ </ShardInput>
173−
171+
174− <Button
172+ <Button
175− disabled={ !ready }
173+ disabled={ !ready }
176− className={'mt-8'}
174+ className={'mt-8'}
177− prop-size={'lg wide'}
175+ prop-size={'lg wide'}
178− // prop-color={this.buttonTriState(wallet)}
176+ // prop-color={this.buttonTriState(wallet)}
179− onClick={() =>
177+ onClick={() =>
180− this.walletFromShards(shard1, shard2, shard3, pointName)
178+ this.walletFromShards(shard1, shard2, shard3, pointName)
181− }>
179+ }>
182− {'Unlock Wallet →'}
180+ {'Unlock Wallet →'}
183− </Button>
181+ </Button>
184−
182+
185− <Button
183+ <Button
186− className={'mt-4'}
184+ className={'mt-4'}
187− prop-size={'xl wide'}
185+ prop-size={'xl wide'}
188− disabled={ Maybe.Nothing.hasInstance(wallet) }
186+ disabled={ Maybe.Nothing.hasInstance(wallet) }
189− onClick={ () => {
187+ onClick={ () => {
190− popRoute()
188+ popRoute()
191− pushRoute(ROUTE_NAMES.SHIPS)
189+ pushRoute(ROUTE_NAMES.SHIPS)
192− }
193 }
190 }
191+ }
194− >
192+ >
195− { 'Continue →' }
193+ { 'Continue →' }
196− </Button>
194+ </Button>
197
195
198− </Form>
199 </Col>
196 </Col>
200 </Row>
197 </Row>
201 )
198 )
src/views/Ticket.js+50−53
@@ -2,7 +2,7 @@ import Maybe from 'folktale/maybe'
2import React from 'react'
2import React from 'react'
3import { Button } from '../components/Base'
3import { Button } from '../components/Base'
4import { InnerLabel, ValidatedSigil, PointInput, TicketInput } from '../components/Base'
4import { InnerLabel, ValidatedSigil, PointInput, TicketInput } from '../components/Base'
5−import { Row, Col, H1, P, Form } from '../components/Base'
5+import { Row, Col, H1, P } from '../components/Base'
6import * as kg from '../../node_modules/urbit-key-generation/dist/index'
6import * as kg from '../../node_modules/urbit-key-generation/dist/index'
7import * as ob from 'urbit-ob'
7import * as ob from 'urbit-ob'
8
8
@@ -79,60 +79,57 @@ class Ticket extends React.Component {
79 { `Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.` }
79 { `Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.` }
80 </P>
80 </P>
81
81
82− <Form>
82+ <PointInput
83−
84− <PointInput
85− className='mono mt-8'
83+ className='mono mt-8'
86− prop-size='lg'
84+ prop-size='lg'
87− prop-format='innerLabel'
85+ prop-format='innerLabel'
88− type='text'
86+ type='text'
89− autoFocus
87+ autoFocus
90− placeholder={ `e.g. ${phPoint}` }
88+ placeholder={ `e.g. ${phPoint}` }
91− value={ pointName }
89+ value={ pointName }
92− onChange={ this.handlePointNameInput }>
90+ onChange={ this.handlePointNameInput }>
93− <InnerLabel>{ 'Point' }</InnerLabel>
91+ <InnerLabel>{ 'Point' }</InnerLabel>
94− <ValidatedSigil
92+ <ValidatedSigil
95− className={'tr-0 mt-05 mr-0 abs'}
93+ className={'tr-0 mt-05 mr-0 abs'}
96− patp={pointName}
94+ patp={pointName}
97− size={68}
95+ size={68}
98− margin={8} />
96+ margin={8} />
99− </PointInput>
97+ </PointInput>
100−
98+
101−
99+
102− <TicketInput
100+ <TicketInput
103− className='mono mt-8'
101+ className='mono mt-8'
104− prop-size='md'
102+ prop-size='md'
105− prop-format='innerLabel'
103+ prop-format='innerLabel'
106− type='text'
104+ type='text'
107− name='ticket'
105+ name='ticket'
108− placeholder={ `e.g. ${phTick}` }
106+ placeholder={ `e.g. ${phTick}` }
109− value={ ticket }
107+ value={ ticket }
110− onChange={ this.handleTicketInput }>
108+ onChange={ this.handleTicketInput }>
111− <InnerLabel>{ 'Ticket' }</InnerLabel>
109+ <InnerLabel>{ 'Ticket' }</InnerLabel>
112− </TicketInput>
110+ </TicketInput>
113−
111+
114− <Button
112+ <Button
115− className={'mt-8'}
113+ className={'mt-8'}
116− prop-size={'lg wide'}
114+ prop-size={'lg wide'}
117− // prop-color={this.buttonTriState(wallet)}
115+ // prop-color={this.buttonTriState(wallet)}
118− onClick={() => this.walletFromTicket(ticket, pointName)}>
116+ onClick={() => this.walletFromTicket(ticket, pointName)}>
119− {'Unlock Wallet →'}
117+ {'Unlock Wallet →'}
120− </Button>
118+ </Button>
121−
119+
122− <Button
120+ <Button
123− className={'mt-4'}
121+ className={'mt-4'}
124− prop-size={'xl wide'}
122+ prop-size={'xl wide'}
125− disabled={ Maybe.Nothing.hasInstance(wallet) }
123+ disabled={ Maybe.Nothing.hasInstance(wallet) }
126− onClick={ () => {
124+ onClick={ () => {
127− popRoute()
125+ popRoute()
128− pushRoute(ROUTE_NAMES.SHIPS)
126+ pushRoute(ROUTE_NAMES.SHIPS)
129− }
130 }
127 }
128+ }
131− >
129+ >
132− { 'Continue →' }
130+ { 'Continue →' }
133− </Button>
131+ </Button>
134
132
135− </Form>
136 </Col>
133 </Col>
137 </Row>
134 </Row>
138 )
135 )
src/views/Transfer.js+52−55
@@ -4,7 +4,7 @@ import * as azimuth from 'azimuth-js'
4import * as ob from 'urbit-ob'
4import * as ob from 'urbit-ob'
5
5
6import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
6import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
7−import { AddressInput, Form, Warning, H3 } from '../components/Base'
7+import { AddressInput, Warning, H3 } from '../components/Base'
8import StatelessTransaction from '../components/StatelessTransaction'
8import StatelessTransaction from '../components/StatelessTransaction'
9
9
10import { BRIDGE_ERROR } from '../lib/error'
10import { BRIDGE_ERROR } from '../lib/error'
@@ -265,60 +265,57 @@ class Transfer extends React.Component {
265 You may cancel the transfer until the transfer is accepted.`
265 You may cancel the transfer until the transfer is accepted.`
266 }
266 }
267 </P>
267 </P>
268− <Form>
269− <AddressInput
268+ <AddressInput
270− className={'mono mt-8'}
269+ className={'mono mt-8'}
271− prop-size='lg'
270+ prop-size='lg'
272− prop-format='innerLabel'
271+ prop-format='innerLabel'
273− value={ state.receivingAddress }
272+ value={ state.receivingAddress }
274− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
273+ placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
275− onChange={ v => this.handleAddressInput(v) }>
274+ onChange={ v => this.handleAddressInput(v) }>
276− <InnerLabel>{ 'New ownership address' }</InnerLabel>
275+ <InnerLabel>{ 'New ownership address' }</InnerLabel>
277− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
276+ <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
278− </AddressInput>
277+ </AddressInput>
279−
278+
280− <Anchor
279+ <Anchor
281− className={'mt-1'}
280+ className={'mt-1'}
282− prop-size={'s'}
281+ prop-size={'s'}
283− disabled={!isValidAddress(state.receivingAddress)}
282+ disabled={!isValidAddress(state.receivingAddress)}
284− target={'_blank'}
283+ target={'_blank'}
285− href={`https://etherscan.io/address/${state.receivingAddress}`}>
284+ href={`https://etherscan.io/address/${state.receivingAddress}`}>
286− {'View on Etherscan ↗'}
285+ {'View on Etherscan ↗'}
287− </Anchor>
286+ </Anchor>
288−
287+
289− <StatelessTransaction
288+ <StatelessTransaction
290− // Upper scope
289+ // Upper scope
291− web3={props.web3}
290+ web3={props.web3}
292− contracts={props.contracts}
291+ contracts={props.contracts}
293− wallet={props.wallet}
292+ wallet={props.wallet}
294− walletType={props.walletType}
293+ walletType={props.walletType}
295− walletHdPath={props.walletHdPath}
294+ walletHdPath={props.walletHdPath}
296− // Tx
295+ // Tx
297− txn={state.txn}
296+ txn={state.txn}
298− stx={state.stx}
297+ stx={state.stx}
299− // Tx details
298+ // Tx details
300− nonce={state.nonce}
299+ nonce={state.nonce}
301− gasPrice={state.gasPrice}
300+ gasPrice={state.gasPrice}
302− chainId={state.chainId}
301+ chainId={state.chainId}
303− gasLimit={state.gasLimit}
302+ gasLimit={state.gasLimit}
304− // Checks
303+ // Checks
305− userApproval={state.userApproval}
304+ userApproval={state.userApproval}
306− canGenerate={ canGenerate }
305+ canGenerate={ canGenerate }
307− canSign={ canSign }
306+ canSign={ canSign }
308− canApprove={ canApprove }
307+ canApprove={ canApprove }
309− canSend={ canSend }
308+ canSend={ canSend }
310− // Methods
309+ // Methods
311− createUnsignedTxn={this.handleCreateUnsignedTxn}
310+ createUnsignedTxn={this.handleCreateUnsignedTxn}
312− setUserApproval={this.handleSetUserApproval}
311+ setUserApproval={this.handleSetUserApproval}
313− setTxn={this.handleSetTxn}
312+ setTxn={this.handleSetTxn}
314− setStx={this.handleSetStx}
313+ setStx={this.handleSetStx}
315− setNonce={this.handleSetNonce}
314+ setNonce={this.handleSetNonce}
316− setChainId={this.handleSetChainId}
315+ setChainId={this.handleSetChainId}
317− setGasPrice={this.handleSetGasPrice}
316+ setGasPrice={this.handleSetGasPrice}
318− setGasLimit={this.handleSetGasLimit}
317+ setGasLimit={this.handleSetGasLimit}
319− handleSubmit={this.handleSubmit} />
318+ handleSubmit={this.handleSubmit} />
320−
321− </Form>
322
319
323 {
320 {
324 Maybe.Nothing.hasInstance(state.txError)
321 Maybe.Nothing.hasInstance(state.txError)
src/views/Trezor.js+31−34
@@ -3,7 +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'
6+import { InnerLabel, Input } from '../components/Base'
7import TrezorConnect from 'trezor-connect'
7import TrezorConnect from 'trezor-connect'
8import * as secp256k1 from 'secp256k1'
8import * as secp256k1 from 'secp256k1'
9
9
@@ -62,39 +62,36 @@ class Trezor extends React.Component {
62 }
62 }
63 </P>
63 </P>
64
64
65− <Form>
66− <Input
65+ <Input
67− className='pt-8'
66+ className='pt-8'
68− prop-size='md'
67+ prop-size='md'
69− prop-format='innerLabel'
68+ prop-format='innerLabel'
70− name='hdpath'
69+ name='hdpath'
71− value={ hdpath }
70+ value={ hdpath }
72− autocomplete='off'
71+ autocomplete='off'
73− onChange={ this.handleHdPathInput }>
72+ onChange={ this.handleHdPathInput }>
74− <InnerLabel>{'HD Path'}</InnerLabel>
73+ <InnerLabel>{'HD Path'}</InnerLabel>
75− </Input>
74+ </Input>
76−
75+
77− <Button
76+ <Button
78− className={'mt-8'}
77+ className={'mt-8'}
79− prop-size={'wide lg'}
78+ prop-size={'wide lg'}
80− onClick={ this.pollDevice }>
79+ onClick={ this.pollDevice }>
81− { 'Authenticate →' }
80+ { 'Authenticate →' }
82− </Button>
81+ </Button>
83−
82+
84− <Button
83+ <Button
85− className={'mt-8'}
84+ className={'mt-8'}
86− prop-size={'wide lg'}
85+ prop-size={'wide lg'}
87− disabled={ Maybe.Nothing.hasInstance(wallet) }
86+ disabled={ Maybe.Nothing.hasInstance(wallet) }
88− onClick={
87+ onClick={
89− () => {
88+ () => {
90− popRoute()
89+ popRoute()
91− pushRoute(ROUTE_NAMES.SHIPS)
90+ pushRoute(ROUTE_NAMES.SHIPS)
92− }
91+ }
93− }>
92+ }>
94− { 'Continue →' }
93+ { 'Continue →' }
95− </Button>
94+ </Button>
96−
97− </Form>
98
95
99 </Col>
96 </Col>
100 </Row>
97 </Row>
src/views/ViewPoint.js+29−31
@@ -4,7 +4,7 @@ import * as ob from 'urbit-ob'
4
4
5import { Button } from '../components/Base'
5import { Button } from '../components/Base'
6import { Row, Col, H1, P } from '../components/Base'
6import { Row, Col, H1, P } from '../components/Base'
7−import { InnerLabel, PointInput, ValidatedSigil, Form } from '../components/Base'
7+import { InnerLabel, PointInput, ValidatedSigil } from '../components/Base'
8
8
9import { ROUTE_NAMES } from '../lib/router'
9import { ROUTE_NAMES } from '../lib/router'
10
10
@@ -48,38 +48,36 @@ class ViewPoint extends React.Component {
48 { "Enter a point name to view its public information." }
48 { "Enter a point name to view its public information." }
49 </P>
49 </P>
50
50
51− <Form>
52− <PointInput
51+ <PointInput
53− autoFocus
52+ autoFocus
54− prop-size='lg'
53+ prop-size='lg'
55− prop-format='innerLabel'
54+ prop-format='innerLabel'
56− className='mono'
55+ className='mono'
57− placeholder='e.g. ~zod'
56+ placeholder='e.g. ~zod'
58− onChange={ this.handlePointInput }
57+ onChange={ this.handlePointInput }
59− value={pointName}>
58+ value={pointName}>
60− <InnerLabel>{'Point Name'}</InnerLabel>
59+ <InnerLabel>{'Point Name'}</InnerLabel>
61− <ValidatedSigil
60+ <ValidatedSigil
62− className={'tr-0 mt-05 mr-0 abs'}
61+ className={'tr-0 mt-05 mr-0 abs'}
63− patp={pointName}
62+ patp={pointName}
64− show
63+ show
65− size={68}
64+ size={68}
66− margin={8} />
65+ margin={8} />
67− </PointInput>
66+ </PointInput>
68
67
69− <Button
68+ <Button
70− className={'mt-8'}
69+ className={'mt-8'}
71− disabled={ valid === false }
70+ disabled={ valid === false }
72− onClick={
71+ onClick={
73− () => {
72+ () => {
74− setPointCursor(Maybe.Just(ob.patp2dec(pointName)))
73+ setPointCursor(Maybe.Just(ob.patp2dec(pointName)))
75− popRoute()
74+ popRoute()
76− pushRoute(ROUTE_NAMES.SHIP)
75+ pushRoute(ROUTE_NAMES.SHIP)
77− }
78 }
76 }
77+ }
79− >
78+ >
80− { 'Continue →' }
79+ { 'Continue →' }
81− </Button>
80+ </Button>
82− </Form>
83 </Col>
81 </Col>
84 </Row>
82 </Row>
85 )
83 )
src/views/Wallet.js+23−8
@@ -83,14 +83,6 @@ class Wallet extends React.Component {
83
83
84 <DropdownDivider />
84 <DropdownDivider />
85
85
86− <DropdownItem
87− onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.MNEMONIC)) }
88− >
89− { 'BIP39 Mnemonic' }
90− </DropdownItem>
91−
92− <DropdownDivider />
93−
94 <DropdownItem
86 <DropdownItem
95 onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.LEDGER)) }
87 onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.LEDGER)) }
96 >
88 >
@@ -109,6 +101,25 @@ class Wallet extends React.Component {
109 { 'Trezor' }
101 { 'Trezor' }
110 </DropdownItem>
102 </DropdownItem>
111
103
104+ <DropdownDivider />
105+
106+ <DropdownItem
107+ onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.MNEMONIC)) }
108+ >
109+ { 'BIP39 Mnemonic' }
110+ </DropdownItem>
111+
112+ <DropdownItem
113+ onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.PRIVATE_KEY)) }
114+ >
115+ { 'Ethereum Private Key' }
116+ </DropdownItem>
117+
118+ <DropdownItem
119+ onClick={ () => this.selectClose(() => props.setWalletType(WALLET_NAMES.KEYSTORE)) }
120+ >
121+ { 'Ethereum Keystore File' }
122+ </DropdownItem>
112
123
113
124
114
125
@@ -128,6 +139,10 @@ class Wallet extends React.Component {
128 ? ROUTE_NAMES.LEDGER
139 ? ROUTE_NAMES.LEDGER
129 : props.walletType === WALLET_NAMES.TREZOR
140 : props.walletType === WALLET_NAMES.TREZOR
130 ? ROUTE_NAMES.TREZOR
141 ? ROUTE_NAMES.TREZOR
142+ : props.walletType === WALLET_NAMES.PRIVATE_KEY
143+ ? ROUTE_NAMES.PRIVATE_KEY
144+ : props.walletType === WALLET_NAMES.KEYSTORE
145+ ? ROUTE_NAMES.KEYSTORE
131 : ROUTE_NAMES.DEFAULT
146 : ROUTE_NAMES.DEFAULT
132 )
147 )
133 }
148 }