on master help sign in
feat: run 'npm run lint:fix' and install precommit hook
82e8d5bMatt Condon (shrugs)
package.json+2−1
@@ -63,7 +63,8 @@
63 "pilot:cleanup": "pkill -f ganache-cli",
63 "pilot:cleanup": "pkill -f ganache-cli",
64 "lint": "prettier src/**/*.js",
64 "lint": "prettier src/**/*.js",
65 "lint:check": "prettier -c 'src/**/*.js'",
65 "lint:check": "prettier -c 'src/**/*.js'",
66− "lint:fix": "prettier --write 'src/**/*.js'"
66+ "lint:fix": "prettier --write 'src/**/*.js'",
67+ "precommit": "npm-run-all lint:check"
67 },
68 },
68 "devDependencies": {
69 "devDependencies": {
69 "eslint-plugin-prettier": "^3.1.0",
70 "eslint-plugin-prettier": "^3.1.0",
src/Root.js+31−37
@@ -1,24 +1,24 @@
1−import 'babel-polyfill' // required for @ledgerhq/hw-transport-u2f
1+import 'babel-polyfill'; // required for @ledgerhq/hw-transport-u2f
2
2
3−import React from 'react'
3+import React from 'react';
4−import { get } from 'lodash'
4+import { get } from 'lodash';
5
5
6−import Bridge from './bridge/Bridge'
6+import Bridge from './bridge/Bridge';
7−import Walletgen from './walletgen/Walletgen'
7+import Walletgen from './walletgen/Walletgen';
8// import { AppNavigation } from './common/components/AppNavigation'
8// import { AppNavigation } from './common/components/AppNavigation'
9
9
10−import './common/style/index.css'
10+import './common/style/index.css';
11−import './bridge/style/index.css'
11+import './bridge/style/index.css';
12
12
13class Root extends React.Component {
13class Root extends React.Component {
14 constructor(props) {
14 constructor(props) {
15− super(props)
15+ super(props);
16
16
17 this.state = {
17 this.state = {
18− appView: "bridge"
18+ appView: 'bridge',
19− }
19+ };
20
20
21− this.setView = this.setView.bind(this)
21+ this.setView = this.setView.bind(this);
22 }
22 }
23
23
24 /*
24 /*
@@ -36,57 +36,51 @@ class Root extends React.Component {
36 */
36 */
37
37
38 isWalletgenStyle(styleSheet) {
38 isWalletgenStyle(styleSheet) {
39− return get(styleSheet, 'cssRules[0].selectorText', null) === "walletgen"
39+ return get(styleSheet, 'cssRules[0].selectorText', null) === 'walletgen';
40 }
40 }
41
41
42 isBridgeStyle(styleSheet) {
42 isBridgeStyle(styleSheet) {
43− return get(styleSheet, 'cssRules[0].selectorText', null) === "bridge"
43+ return get(styleSheet, 'cssRules[0].selectorText', null) === 'bridge';
44 }
44 }
45
45
46 disableStylesheets(appView) {
46 disableStylesheets(appView) {
47 for (var i = 0; i < document.styleSheets.length; i++) {
47 for (var i = 0; i < document.styleSheets.length; i++) {
48− let styleSheet = document.styleSheets[i]
48+ let styleSheet = document.styleSheets[i];
49−
49+
50− if (appView === "bridge") {
50+ if (appView === 'bridge') {
51− if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = true
51+ if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = true;
52− if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = false
52+ if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = false;
53− } else if (appView === "walletgen") {
53+ } else if (appView === 'walletgen') {
54− if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = false
54+ if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = false;
55− if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = true
55+ if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = true;
56 }
56 }
57 }
57 }
58 }
58 }
59
59
60 componentDidMount() {
60 componentDidMount() {
61− this.disableStylesheets(this.state.appView)
61+ this.disableStylesheets(this.state.appView);
62 }
62 }
63
63
64 setView(appView) {
64 setView(appView) {
65− this.disableStylesheets(appView)
65+ this.disableStylesheets(appView);
66
66
67 this.setState({
67 this.setState({
68− appView
68+ appView,
69− })
69+ });
70 }
70 }
71
71
72 render() {
72 render() {
73 const { appView } = this.state;
73 const { appView } = this.state;
74
74
75 switch (appView) {
75 switch (appView) {
76− case "walletgen":
76+ case 'walletgen':
77− return <Walletgen
78− appView={appView}
79− setView={this.setView} />
77+ return <Walletgen appView={appView} setView={this.setView} />;
80− case "bridge":
78+ case 'bridge':
81− return <Bridge
82− appView={appView}
83− setView={this.setView} />
79+ return <Bridge appView={appView} setView={this.setView} />;
84 default:
80 default:
85− throw new Error(`Unknown appView ${appView}`)
81+ throw new Error(`Unknown appView ${appView}`);
86 }
82 }
87 }
83 }
88}
84}
89
85
90−export {
86+export { Root };
91− Root
92−}
src/bridge/Bridge.js+174−176
@@ -1,60 +1,55 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import * as lodash from 'lodash'
2+import * as lodash from 'lodash';
3−import * as azimuth from 'azimuth-js'
3+import * as azimuth from 'azimuth-js';
4−import { Stack } from 'immutable'
4+import { Stack } from 'immutable';
5−import { CONTRACT_ADDRESSES } from './lib/contracts'
5+import { CONTRACT_ADDRESSES } from './lib/contracts';
6−import React from 'react'
6+import React from 'react';
7−
7+
8−import Web3 from 'web3'
8+import Web3 from 'web3';
9−import Footer from './components/Footer'
9+import Footer from './components/Footer';
10−import Header from './components/Header'
10+import Header from './components/Header';
11−import { Container, Row, Col } from './components/Base'
11+import { Container, Row, Col } from './components/Base';
12−
12+
13−import { ROUTE_NAMES, router } from './lib/router'
13+import { ROUTE_NAMES, router } from './lib/router';
14−import { NETWORK_NAMES } from './lib/network'
14+import { NETWORK_NAMES } from './lib/network';
15import {
15import {
16 WALLET_NAMES,
16 WALLET_NAMES,
17 DEFAULT_HD_PATH,
17 DEFAULT_HD_PATH,
18 walletFromMnemonic,
18 walletFromMnemonic,
19− addressFromSecp256k1Public
19+ addressFromSecp256k1Public,
20−} from './lib/wallet'
20+} from './lib/wallet';
21−import { BRIDGE_ERROR } from './lib/error'
21+import { BRIDGE_ERROR } from './lib/error';
22
22
23−const initWeb3 = (networkType) => {
23+const initWeb3 = networkType => {
24 if (networkType === NETWORK_NAMES.MAINNET) {
24 if (networkType === NETWORK_NAMES.MAINNET) {
25− const endpoint =
26− `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
25+ const endpoint = `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
27
26
28− const provider = new Web3.providers.HttpProvider(endpoint)
27+ const provider = new Web3.providers.HttpProvider(endpoint);
29− const web3 = new Web3(provider)
28+ const web3 = new Web3(provider);
30− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET)
29+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
31− return {web3: web3, contracts: contracts}
30+ return { web3: web3, contracts: contracts };
32 } else if (networkType === NETWORK_NAMES.LOCAL) {
31 } else if (networkType === NETWORK_NAMES.LOCAL) {
33− const protocol =
34− process.env.NODE_ENV === 'development'
32+ const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
35− ? 'ws'
36− : 'wss'
37− const endpoint = `${protocol}://localhost:8545`
33+ const endpoint = `${protocol}://localhost:8545`;
38− const provider = new Web3.providers.WebsocketProvider(endpoint)
34+ const provider = new Web3.providers.WebsocketProvider(endpoint);
39− const web3 = new Web3(provider)
35+ const web3 = new Web3(provider);
40− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
36+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
41− return {web3: web3, contracts: contracts}
37+ return { web3: web3, contracts: contracts };
42 }
38 }
43−}
39+};
44
40
45class Bridge extends React.Component {
41class Bridge extends React.Component {
46−
47 constructor(props) {
42 constructor(props) {
48− super(props)
43+ super(props);
49
44
50 const networkType =
45 const networkType =
51− process.env.NODE_ENV === 'development'
46+ process.env.NODE_ENV === 'development'
52− ? NETWORK_NAMES.LOCAL
47+ ? NETWORK_NAMES.LOCAL
53− : NETWORK_NAMES.MAINNET
48+ : NETWORK_NAMES.MAINNET;
54
49
55 // Sidestepping full network selection to allow for point viewing,
50 // Sidestepping full network selection to allow for point viewing,
56 // but still respecting the networkType
51 // but still respecting the networkType
57− const { web3, contracts } = initWeb3(networkType)
52+ const { web3, contracts } = initWeb3(networkType);
58
53
59 // NB (jtobin):
54 // NB (jtobin):
60 //
55 //
@@ -71,7 +66,7 @@ class Bridge extends React.Component {
71
66
72 this.state = {
67 this.state = {
73 // routes
68 // routes
74− routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
69+ routeCrumbs: Stack([ROUTE_NAMES.LANDING]),
75 routeData: {},
70 routeData: {},
76 // network
71 // network
77 networkType: networkType,
72 networkType: networkType,
@@ -90,129 +85,128 @@ class Bridge extends React.Component {
90 pointCache: {},
85 pointCache: {},
91 // txn
86 // txn
92 txnHashCursor: Nothing(),
87 txnHashCursor: Nothing(),
93− txnConfirmations: {}
88+ txnConfirmations: {},
94− }
89+ };
95−
90+
96− this.pushRoute = this.pushRoute.bind(this)
91+ this.pushRoute = this.pushRoute.bind(this);
97− this.popRoute = this.popRoute.bind(this)
92+ this.popRoute = this.popRoute.bind(this);
98− this.skipRoute = this.skipRoute.bind(this)
93+ this.skipRoute = this.skipRoute.bind(this);
99− this.setNetworkType = this.setNetworkType.bind(this)
94+ this.setNetworkType = this.setNetworkType.bind(this);
100− this.setNetwork = this.setNetwork.bind(this)
95+ this.setNetwork = this.setNetwork.bind(this);
101− this.setWalletType = this.setWalletType.bind(this)
96+ this.setWalletType = this.setWalletType.bind(this);
102− this.setWallet = this.setWallet.bind(this)
97+ this.setWallet = this.setWallet.bind(this);
103− this.setWalletHdPath = this.setWalletHdPath.bind(this)
98+ this.setWalletHdPath = this.setWalletHdPath.bind(this);
104− this.setUrbitWallet = this.setUrbitWallet.bind(this)
99+ this.setUrbitWallet = this.setUrbitWallet.bind(this);
105− this.setAuthMnemonic = this.setAuthMnemonic.bind(this)
100+ this.setAuthMnemonic = this.setAuthMnemonic.bind(this);
106− this.setPointCursor = this.setPointCursor.bind(this)
101+ this.setPointCursor = this.setPointCursor.bind(this);
107− this.setTxnHashCursor = this.setTxnHashCursor.bind(this)
102+ this.setTxnHashCursor = this.setTxnHashCursor.bind(this);
108− this.setTxnConfirmations = this.setTxnConfirmations.bind(this)
103+ this.setTxnConfirmations = this.setTxnConfirmations.bind(this);
109− this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this)
104+ this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this);
110− this.addToPointCache = this.addToPointCache.bind(this)
105+ this.addToPointCache = this.addToPointCache.bind(this);
111 }
106 }
112
107
113 componentDidMount() {
108 componentDidMount() {
114 window.history.pushState(null, null, null);
109 window.history.pushState(null, null, null);
115
110
116− window.onpopstate = (e) => {
111+ window.onpopstate = e => {
117 window.history.pushState(null, null, null);
112 window.history.pushState(null, null, null);
118 this.popRoute();
113 this.popRoute();
119− }
114+ };
120
115
121− // NB (jtobin):
116+ // NB (jtobin):
122− //
117+ //
123− // If running in development, the following can be tweaked to get you set
118+ // If running in development, the following can be tweaked to get you set
124− // up with a desirable initial state.
119+ // up with a desirable initial state.
125−
120+
126− // if (process.env.NODE_ENV === 'development') {
121+ // if (process.env.NODE_ENV === 'development') {
127− //
122+ //
128− // const socket = 'ws://localhost:8545'
123+ // const socket = 'ws://localhost:8545'
129− // const provider = new Web3.providers.WebsocketProvider(socket)
124+ // const provider = new Web3.providers.WebsocketProvider(socket)
130− // const web3 = new Web3(provider)
125+ // const web3 = new Web3(provider)
131− // const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
126+ // const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
132− //
127+ //
133− // const mnemonic = process.env.REACT_APP_DEV_MNEMONIC
128+ // const mnemonic = process.env.REACT_APP_DEV_MNEMONIC
134− // const hdpath = process.env.REACT_APP_HD_PATH
129+ // const hdpath = process.env.REACT_APP_HD_PATH
135− //
130+ //
136− // this.setState({
131+ // this.setState({
137− // routeCrumbs: Stack([
132+ // routeCrumbs: Stack([
138− // // ROUTE_NAMES.CREATE_GALAXY,
133+ // // ROUTE_NAMES.CREATE_GALAXY,
139− // // ROUTE_NAMES.SHIP,
134+ // // ROUTE_NAMES.SHIP,
140− // ROUTE_NAMES.SHIPS,
135+ // ROUTE_NAMES.SHIPS,
141− // // ROUTE_NAMES.MNEMONIC,
136+ // // ROUTE_NAMES.MNEMONIC,
142− // ROUTE_NAMES.WALLET,
137+ // ROUTE_NAMES.WALLET,
143− // ROUTE_NAMES.NETWORK,
138+ // ROUTE_NAMES.NETWORK,
144− // ROUTE_NAMES.LANDING
139+ // ROUTE_NAMES.LANDING
145− // ]),
140+ // ]),
146− // networkType: NETWORK_NAMES.LOCAL,
141+ // networkType: NETWORK_NAMES.LOCAL,
147− // pointCursor: Just(0),
142+ // pointCursor: Just(0),
148− // web3: Just(web3),
143+ // web3: Just(web3),
149− // contracts: Just(contracts),
144+ // contracts: Just(contracts),
150− // walletType: WALLET_NAMES.MNEMONIC,
145+ // walletType: WALLET_NAMES.MNEMONIC,
151− // wallet: walletFromMnemonic(mnemonic, hdpath),
146+ // wallet: walletFromMnemonic(mnemonic, hdpath),
152− // urbitWallet: Nothing(),
147+ // urbitWallet: Nothing(),
153− // authMnemonic: Just('benefit crew supreme gesture quantum web media hazard theory mercy wing kitten')
148+ // authMnemonic: Just('benefit crew supreme gesture quantum web media hazard theory mercy wing kitten')
154− // })
149+ // })
155− // }
150+ // }
156−}
151+ }
157
152
158 pushRoute(symbol, routeData) {
153 pushRoute(symbol, routeData) {
159 if (lodash.includes(ROUTE_NAMES, symbol)) {
154 if (lodash.includes(ROUTE_NAMES, symbol)) {
160 this.setState((state, _) => ({
155 this.setState((state, _) => ({
161 routeCrumbs: state.routeCrumbs.push(symbol),
156 routeCrumbs: state.routeCrumbs.push(symbol),
162− routeData: routeData
157+ routeData: routeData,
163 }));
158 }));
164
159
165 // Scroll to top of page with each route transition.
160 // Scroll to top of page with each route transition.
166 window.scrollTo(0, 0);
161 window.scrollTo(0, 0);
167−
168 } else {
162 } else {
169− throw BRIDGE_ERROR.INVALID_ROUTE
163+ throw BRIDGE_ERROR.INVALID_ROUTE;
170 }
164 }
171 }
165 }
172
166
173 skipRoute(len) {
167 skipRoute(len) {
174 this.setState((state, _) => ({
168 this.setState((state, _) => ({
175− routeCrumbs: state.routeCrumbs.skip(len)
169+ routeCrumbs: state.routeCrumbs.skip(len),
176− }))
170+ }));
177 }
171 }
178
172
179 popRoute() {
173 popRoute() {
180 this.setState((state, _) => ({
174 this.setState((state, _) => ({
181− routeCrumbs: state.routeCrumbs.shift()
175+ routeCrumbs: state.routeCrumbs.shift(),
182− }))
176+ }));
183 }
177 }
184
178
185 setNetworkType(symbol) {
179 setNetworkType(symbol) {
186 if (lodash.includes(NETWORK_NAMES, symbol)) {
180 if (lodash.includes(NETWORK_NAMES, symbol)) {
187 this.setState({
181 this.setState({
188− networkType: symbol
182+ networkType: symbol,
189− })
183+ });
190 } else {
184 } else {
191− throw BRIDGE_ERROR.INVALID_NETWORK_TYPE
185+ throw BRIDGE_ERROR.INVALID_NETWORK_TYPE;
192 }
186 }
193 }
187 }
194
188
195 setNetworkSeedCache(networkSeed, revision) {
189 setNetworkSeedCache(networkSeed, revision) {
196 this.setState({
190 this.setState({
197 networkSeedCache: networkSeed,
191 networkSeedCache: networkSeed,
198− networkRevisionCache: revision
192+ networkRevisionCache: revision,
199− })
193+ });
200 }
194 }
201
195
202 setNetwork(web3, contracts) {
196 setNetwork(web3, contracts) {
203 this.setState({
197 this.setState({
204 web3: web3,
198 web3: web3,
205− contracts: contracts
199+ contracts: contracts,
206− })
200+ });
207 }
201 }
208
202
209 setWalletType(symbol) {
203 setWalletType(symbol) {
210 if (lodash.includes(WALLET_NAMES, symbol)) {
204 if (lodash.includes(WALLET_NAMES, symbol)) {
211 this.setState({
205 this.setState({
212− walletType: symbol
206+ walletType: symbol,
213− })
207+ });
214 } else {
208 } else {
215− throw BRIDGE_ERROR.INVALID_WALLET_TYPE
209+ throw BRIDGE_ERROR.INVALID_WALLET_TYPE;
216 }
210 }
217 }
211 }
218
212
@@ -225,7 +219,7 @@ class Bridge extends React.Component {
225 }
219 }
226
220
227 setWalletHdPath(walletHdPath) {
221 setWalletHdPath(walletHdPath) {
228− this.setState({ walletHdPath })
222+ this.setState({ walletHdPath });
229 }
223 }
230
224
231 // also sets wallet to ownership address
225 // also sets wallet to ownership address
@@ -243,34 +237,38 @@ class Bridge extends React.Component {
243 return wal;
237 return wal;
244 });
238 });
245 }
239 }
246− this.setState({ urbitWallet, wallet })
240+ this.setState({ urbitWallet, wallet });
247 }
241 }
248
242
249 setAuthMnemonic(authMnemonic) {
243 setAuthMnemonic(authMnemonic) {
250− this.setState({ authMnemonic })
244+ this.setState({ authMnemonic });
251 }
245 }
252
246
253 setPointCursor(pointCursor) {
247 setPointCursor(pointCursor) {
254− this.setState({ pointCursor })
248+ this.setState({ pointCursor });
255 }
249 }
256
250
257 addToPointCache(entry) {
251 addToPointCache(entry) {
258 this.setState((state, _) => ({
252 this.setState((state, _) => ({
259− pointCache: Object.assign(state.pointCache, entry)
253+ pointCache: Object.assign(state.pointCache, entry),
260− }))
254+ }));
261 }
255 }
262
256
263 setTxnHashCursor(txnHashCursor) {
257 setTxnHashCursor(txnHashCursor) {
264− this.setState({ txnHashCursor })
258+ this.setState({ txnHashCursor });
265 }
259 }
266
260
267 setTxnConfirmations(txnHash, txnConfirmations) {
261 setTxnConfirmations(txnHash, txnConfirmations) {
268 this.setState((prevState, _) => {
262 this.setState((prevState, _) => {
269 return prevState.routeCrumbs.peek() === ROUTE_NAMES.SENT_TRANSACTION
263 return prevState.routeCrumbs.peek() === ROUTE_NAMES.SENT_TRANSACTION
264+ ? {
265+ txnConfirmations: {
270− ? {txnConfirmations: { ...prevState.txnConfirmations,
266+ ...prevState.txnConfirmations,
271− [txnHash]: txnConfirmations}}
267+ [txnHash]: txnConfirmations,
268+ },
269+ }
272− : null
270+ : null;
273− })
271+ });
274 }
272 }
275
273
276 render() {
274 render() {
@@ -290,87 +288,87 @@ class Bridge extends React.Component {
290 txnHashCursor,
288 txnHashCursor,
291 txnConfirmations,
289 txnConfirmations,
292 web3,
290 web3,
293− contracts
291+ contracts,
294− } = this.state
292+ } = this.state;
295
293
296− const View = router(routeCrumbs.peek())
294+ const View = router(routeCrumbs.peek());
297
295
298 // For the invite acceptance flow, widen the screen to use the full
296 // For the invite acceptance flow, widen the screen to use the full
299 // container, and hide the breadcrumbs
297 // container, and hide the breadcrumbs
300 const colClass = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
298 const colClass = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
301− ? 'col-md-12'
299+ ? 'col-md-12'
302− : 'col-md-offset-1 col-md-10'
300+ : 'col-md-offset-1 col-md-10';
303
301
304 const colStyle = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
302 const colStyle = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
305− ? {}
303+ ? {}
306− : {maxWidth: '620px'}
304+ : { maxWidth: '620px' };
307
305
308 const showCrumbs = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
306 const showCrumbs = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
309− ? false
307+ ? false
310− : true
308+ : true;
311
309
312 return (
310 return (
313 <Container>
311 <Container>
314 <Row>
312 <Row>
315 <Col className={colClass} style={colStyle}>
313 <Col className={colClass} style={colStyle}>
316 <Header
314 <Header
317− routeCrumbs={ routeCrumbs }
315+ routeCrumbs={routeCrumbs}
318− skipRoute={ this.skipRoute }
316+ skipRoute={this.skipRoute}
319− networkType={ networkType }
317+ networkType={networkType}
320− wallet={ wallet }
318+ wallet={wallet}
321− showCrumbs={ showCrumbs }
319+ showCrumbs={showCrumbs}
322− pointCursor={ pointCursor } />
320+ pointCursor={pointCursor}
323−
321+ />
322+
324− <Row className={ 'row wrapper' }>
323+ <Row className={'row wrapper'}>
325−
326 <View
324 <View
327 // router
325 // router
328− pushRoute={ this.pushRoute }
326+ pushRoute={this.pushRoute}
329− popRoute={ this.popRoute }
327+ popRoute={this.popRoute}
330− routeData={ routeData }
328+ routeData={routeData}
331 // network
329 // network
332− setNetworkType={ this.setNetworkType }
330+ setNetworkType={this.setNetworkType}
333− setNetwork={ this.setNetwork }
331+ setNetwork={this.setNetwork}
334− networkType={ networkType }
332+ networkType={networkType}
335− web3={ web3 }
333+ web3={web3}
336− contracts={ contracts }
334+ contracts={contracts}
337 // wallet
335 // wallet
338− setWalletType={ this.setWalletType }
336+ setWalletType={this.setWalletType}
339− setWalletHdPath={ this.setWalletHdPath }
337+ setWalletHdPath={this.setWalletHdPath}
340− setWallet={ this.setWallet }
338+ setWallet={this.setWallet}
341− walletType={ walletType }
339+ walletType={walletType}
342− walletHdPath={ walletHdPath }
340+ walletHdPath={walletHdPath}
343− wallet={ wallet }
341+ wallet={wallet}
344 // urbit wallet
342 // urbit wallet
345− urbitWallet={ urbitWallet }
343+ urbitWallet={urbitWallet}
346− setUrbitWallet={ this.setUrbitWallet }
344+ setUrbitWallet={this.setUrbitWallet}
347− authMnemonic={ authMnemonic }
345+ authMnemonic={authMnemonic}
348− setAuthMnemonic={ this.setAuthMnemonic }
346+ setAuthMnemonic={this.setAuthMnemonic}
349 // point
347 // point
350− setPointCursor={ this.setPointCursor }
348+ setPointCursor={this.setPointCursor}
351− addToPointCache={ this.addToPointCache }
349+ addToPointCache={this.addToPointCache}
352− pointCursor={ pointCursor }
350+ pointCursor={pointCursor}
353− pointCache={ pointCache }
351+ pointCache={pointCache}
354− networkSeedCache= { networkSeedCache }
352+ networkSeedCache={networkSeedCache}
355− networkRevisionCache={ networkRevisionCache }
353+ networkRevisionCache={networkRevisionCache}
356− setNetworkSeedCache= { this.setNetworkSeedCache }
354+ setNetworkSeedCache={this.setNetworkSeedCache}
357 // txn
355 // txn
358− onSent={ this.setTxnHashCursor }
356+ onSent={this.setTxnHashCursor}
359− setTxnHashCursor={ this.setTxnHashCursor }
357+ setTxnHashCursor={this.setTxnHashCursor}
360− txnHashCursor={ txnHashCursor }
358+ txnHashCursor={txnHashCursor}
361− setTxnConfirmations={ this.setTxnConfirmations }
359+ setTxnConfirmations={this.setTxnConfirmations}
362− txnConfirmations={ txnConfirmations } />
360+ txnConfirmations={txnConfirmations}
361+ />
363
362
364 <div className={'push'} />
363 <div className={'push'} />
365−
366 </Row>
364 </Row>
367
365
368 <Footer />
366 <Footer />
369 </Col>
367 </Col>
370 </Row>
368 </Row>
371 </Container>
369 </Container>
372− )
370+ );
373 }
371 }
374}
372}
375
373
376−export default Bridge
374+export default Bridge;
src/bridge/components/Anchor.js+13−18
@@ -1,33 +1,28 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−import ProtoAnchor from './ProtoAnchor'
4+import ProtoAnchor from './ProtoAnchor';
5−
6
5
7const Anchor = props => {
6const Anchor = props => {
8−
9 return (
7 return (
10− <ProtoAnchor
11− {...props}
12− prop-disabled={`${props['prop-disabled']}`}>
8+ <ProtoAnchor {...props} prop-disabled={`${props['prop-disabled']}`}>
13− { props.children }
9+ {props.children}
14 </ProtoAnchor>
10 </ProtoAnchor>
15− )
11+ );
16−}
12+};
17
13
18Anchor.propTypes = {
14Anchor.propTypes = {
19− 'prop-disabled': PropTypes.bool,
15+ 'prop-disabled': PropTypes.bool,
20− 'prop-color': PropTypes.string,
16+ 'prop-color': PropTypes.string,
21− 'prop-size': PropTypes.string,
17+ 'prop-size': PropTypes.string,
22− 'prop-type': PropTypes.string,
18+ 'prop-type': PropTypes.string,
23};
19};
24
20
25Anchor.defaultProps = {
21Anchor.defaultProps = {
26− 'prop-disabled': false,
22+ 'prop-disabled': false,
27− 'prop-color': 'black',
23+ 'prop-color': 'black',
28− 'prop-size': 'md',
24+ 'prop-size': 'md',
29− 'prop-type': 'link',
25+ 'prop-type': 'link',
30};
26};
31
27
32−
33export default Anchor;
28export default Anchor;
src/bridge/components/Base.js+40−87
@@ -9,12 +9,10 @@ import {
9 validateTicket,
9 validateTicket,
10 validateNetworkKey,
10 validateNetworkKey,
11 validateNetworkSeed,
11 validateNetworkSeed,
12− validateShard
12+ validateShard,
13} from '../lib/validators';
13} from '../lib/validators';
14
14
15−import {
16− prependSig,
17−} from '../lib/transformers';
15+import { prependSig } from '../lib/transformers';
18
16
19// Buttons
17// Buttons
20import Button from './Button';
18import Button from './Button';
@@ -25,7 +23,7 @@ import Anchor from './Anchor';
25// import ToggleButton from './ToggleButton';
23// import ToggleButton from './ToggleButton';
26
24
27// Input
25// Input
28−import Form from './Form'
26+import Form from './Form';
29import Label from './Label';
27import Label from './Label';
30import InnerLabel from './InnerLabel';
28import InnerLabel from './InnerLabel';
31import InputCaption from './InputCaption';
29import InputCaption from './InputCaption';
@@ -59,120 +57,84 @@ import H2 from './H2';
59import H3 from './H3';
57import H3 from './H3';
60import H4 from './H4';
58import H4 from './H4';
61import P from './P';
59import P from './P';
62−import Code from './Code'
60+import Code from './Code';
63−import Warning from './Warning'
61+import Warning from './Warning';
64
62
65// Misc
63// Misc
66import ValidatedSigil from './ValidatedSigil';
64import ValidatedSigil from './ValidatedSigil';
67import ValidatedBlockie from './ValidatedBlockie';
65import ValidatedBlockie from './ValidatedBlockie';
68−import Chevron from './icons/Chevron'
66+import Chevron from './icons/Chevron';
69−import ShowBlockie from './ShowBlockie'
67+import ShowBlockie from './ShowBlockie';
70−import Passport from './Passport'
68+import Passport from './Passport';
71
69
72// HOC validated form components
70// HOC validated form components
73
71
74const RequiredInput = advancedInput({
72const RequiredInput = advancedInput({
75 WrappedComponent: Input,
73 WrappedComponent: Input,
76− validators: [
77− validateNotEmpty,
74+ validators: [validateNotEmpty],
78− ],
79});
75});
80
76
81−
82const NetworkKeyInput = advancedInput({
77const NetworkKeyInput = advancedInput({
83 WrappedComponent: Input,
78 WrappedComponent: Input,
84− validators: [
85− validateNotEmpty,
86− validateNetworkKey
79+ validators: [validateNotEmpty, validateNetworkKey],
87− ],
88});
80});
89
81
90−
91const NetworkSeedInput = advancedInput({
82const NetworkSeedInput = advancedInput({
92 WrappedComponent: Input,
83 WrappedComponent: Input,
93− validators: [
94− validateNotEmpty,
95− validateNetworkSeed
84+ validators: [validateNotEmpty, validateNetworkSeed],
96− ],
97});
85});
98
86
99−
100const AddressInput = advancedInput({
87const AddressInput = advancedInput({
101 WrappedComponent: Input,
88 WrappedComponent: Input,
102− validators: [
103− validateEthereumAddress,
89+ validators: [validateEthereumAddress, validateNotEmpty],
104− validateNotEmpty,
105− ],
106});
90});
107
91
108−
109const MnemonicInput = advancedInput({
92const MnemonicInput = advancedInput({
110 WrappedComponent: Textarea,
93 WrappedComponent: Textarea,
111− validators: [
112− validateMnemonic,
94+ validators: [validateMnemonic, validateNotEmpty],
113− validateNotEmpty,
114− ],
115});
95});
116
96
117const GalaxyInput = advancedInput({
97const GalaxyInput = advancedInput({
118 WrappedComponent: Input,
98 WrappedComponent: Input,
119− validators: [
120− validateGalaxy,
121− validateNotEmpty,
99+ validators: [validateGalaxy, validateNotEmpty],
122− ],
123− transformers: [
100+ transformers: [prependSig],
124− prependSig,
125− ]
126});
101});
127
102
128const PointInput = advancedInput({
103const PointInput = advancedInput({
129 WrappedComponent: Input,
104 WrappedComponent: Input,
130− validators: [
131− validatePoint,
132− validateNotEmpty,
105+ validators: [validatePoint, validateNotEmpty],
133− ],
134− transformers: [
106+ transformers: [prependSig],
135− prependSig,
136− ]
137});
107});
138
108
139const TicketInput = advancedInput({
109const TicketInput = advancedInput({
140 WrappedComponent: Input,
110 WrappedComponent: Input,
141− validators: [
142− validateTicket,
143− validateNotEmpty,
111+ validators: [validateTicket, validateNotEmpty],
144− ],
145− transformers: [
112+ transformers: [prependSig],
146− prependSig,
147− ]
148});
113});
149
114
150−const VerifyTicketInput = matchingTicket => advancedInput({
115+const VerifyTicketInput = matchingTicket =>
116+ advancedInput({
151− WrappedComponent: Input,
117+ WrappedComponent: Input,
152− validators: [
118+ validators: [
153− validateTicket,
119+ validateTicket,
154− validateNotEmpty,
120+ validateNotEmpty,
155−
121+
156− // TODO: This should be in /lib/validators, but we'd need a way for
122+ // TODO: This should be in /lib/validators, but we'd need a way for
157− // validators to compare against dynamic values
123+ // validators to compare against dynamic values
124+ m =>
158− m => simpleValidatorWrapper({
125+ simpleValidatorWrapper({
159− prevMessage: m,
126+ prevMessage: m,
160− validator: d => d === matchingTicket,
127+ validator: d => d === matchingTicket,
161− errorMessage: 'This does not match the provided master ticket'
128+ errorMessage: 'This does not match the provided master ticket',
162− })
129+ }),
163− ],
130+ ],
164− transformers: [
131+ transformers: [prependSig],
165− prependSig,
166− ]
167−});
132+ });
168
133
169const ShardInput = advancedInput({
134const ShardInput = advancedInput({
170 WrappedComponent: Input,
135 WrappedComponent: Input,
171− validators: [
172− validateShard
136+ validators: [validateShard],
173− ],
174− transformers: [
137+ transformers: [],
175− ]
176});
138});
177
139
178export {
140export {
@@ -181,7 +143,6 @@ export {
181 UploadButton,
143 UploadButton,
182 Anchor,
144 Anchor,
183 InputCaption,
145 InputCaption,
184−
185 Form,
146 Form,
186 Label,
147 Label,
187 Input,
148 Input,
@@ -197,29 +158,22 @@ export {
197 RequiredInput,
158 RequiredInput,
198 NetworkKeyInput,
159 NetworkKeyInput,
199 NetworkSeedInput,
160 NetworkSeedInput,
200−
201 advancedInput,
161 advancedInput,
202−
203 ValidatedSigil,
162 ValidatedSigil,
204 ValidatedBlockie,
163 ValidatedBlockie,
205 ShowBlockie,
164 ShowBlockie,
206−
207 DropdownItem,
165 DropdownItem,
208 DropdownDivider,
166 DropdownDivider,
209 InnerLabelDropdown,
167 InnerLabelDropdown,
210−
211 HorizontalSelector,
168 HorizontalSelector,
212−
213 Breadcrumb,
169 Breadcrumb,
214 BreadcrumbItem,
170 BreadcrumbItem,
215−
216 Container,
171 Container,
217 Col,
172 Col,
218 Row,
173 Row,
219 // FlexRow,
174 // FlexRow,
220 // FlexCol,
175 // FlexCol,
221 Spacing,
176 Spacing,
222−
223 H1,
177 H1,
224 H2,
178 H2,
225 H3,
179 H3,
@@ -227,7 +181,6 @@ export {
227 P,
181 P,
228 Code,
182 Code,
229 Warning,
183 Warning,
230−
231 Chevron,
184 Chevron,
232− Passport
185+ Passport,
233};
186};
src/bridge/components/Blockie.js+13−8
@@ -1,12 +1,17 @@
1−import React from 'react'
1+import React from 'react';
2−import makeBlockie from 'ethereum-blockies-base64'
2+import makeBlockie from 'ethereum-blockies-base64';
3
3
4−const isValidAddress = (address) =>
4+const isValidAddress = address => /^0x[0-9a-fA-F]{40}$/.test(address);
5− /^0x[0-9a-fA-F]{40}$/.test(address)
6
5
7const Blockie = props =>
6const Blockie = props =>
8− isValidAddress(props.address)
7+ isValidAddress(props.address) ? (
8+ <img
9− ? <img className={`s-10 ${props.className}`} alt={ props.address } src={ makeBlockie(props.address) } />
9+ className={`s-10 ${props.className}`}
10+ alt={props.address}
11+ src={makeBlockie(props.address)}
12+ />
13+ ) : (
10− : <div className={`s-10 ${props.className}`} />
14+ <div className={`s-10 ${props.className}`} />
15+ );
11
16
12−export default Blockie
17+export default Blockie;
src/bridge/components/Breadcrumb.js+4−7
@@ -1,12 +1,9 @@
1import React from 'react';
1import React from 'react';
2
2
3−const Breadcrumb = ({ disabled, onClick, className, children }) =>
3+const Breadcrumb = ({ disabled, onClick, className, children }) => (
4− <div
5− className={`flex ${className}`}
4+ <div className={`flex ${className}`} onClick={onClick} disabled={disabled}>
6− onClick={onClick}
7− disabled={disabled}
8− >
9− { children }
5+ {children}
10 </div>
6 </div>
7+);
11
8
12export default Breadcrumb;
9export default Breadcrumb;
src/bridge/components/BreadcrumbItem.js+4−7
@@ -1,12 +1,9 @@
1import React from 'react';
1import React from 'react';
2
2
3−const BreadcrumbItem = ({ disabled, onClick, className, children }) =>
3+const BreadcrumbItem = ({ disabled, onClick, className, children }) => (
4− <div
5− className={`${className}`}
4+ <div className={`${className}`} onClick={onClick} disabled={disabled}>
6− onClick={onClick}
7− disabled={disabled}
8− >
9− { children }
5+ {children}
10 </div>
6 </div>
7+);
11
8
12export default BreadcrumbItem;
9export default BreadcrumbItem;
src/bridge/components/Button.js+3−12
@@ -1,17 +1,10 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3−import ProtoButton from './ProtoButton'
3+import ProtoButton from './ProtoButton';
4−
5
4
6const Button = props => {
5const Button = props => {
7− return (
8− <ProtoButton
9− {...props}>
10− { props.children }
6+ return <ProtoButton {...props}>{props.children}</ProtoButton>;
11− </ProtoButton>
12− )
13−}
7+};
14−
15
8
16Button.propTypes = {
9Button.propTypes = {
17 'prop-color': PropTypes.string,
10 'prop-color': PropTypes.string,
@@ -19,12 +12,10 @@ Button.propTypes = {
19 'prop-type': PropTypes.string,
12 'prop-type': PropTypes.string,
20};
13};
21
14
22−
23Button.defaultProps = {
15Button.defaultProps = {
24 'prop-color': 'blue',
16 'prop-color': 'blue',
25 'prop-size': 'md',
17 'prop-size': 'md',
26 'prop-type': 'solid',
18 'prop-type': 'solid',
27};
19};
28
20
29−
30export default Button;
21export default Button;
src/bridge/components/Checkbox.js+22−19
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
2
2
3const CheckBox = ({
3const CheckBox = ({
4 name,
4 name,
@@ -10,38 +10,41 @@ const CheckBox = ({
10 state,
10 state,
11 children,
11 children,
12 childContainerClassname,
12 childContainerClassname,
13− disabled
13+ disabled,
14}) => {
14}) => {
15−
16− const isCheckedClassname = state === true
15+ const isCheckedClassname = state === true ? 'cb-on' : 'cb-off';
17− ? 'cb-on'
18− : 'cb-off';
19−
20
16
21 return (
17 return (
22 <div className={`cb flex items-center ${isCheckedClassname} ${className}`}>
18 <div className={`cb flex items-center ${isCheckedClassname} ${className}`}>
23 <label className={'s-6 m-0 mr-4 rel outline-blue'}>
19 <label className={'s-6 m-0 mr-4 rel outline-blue'}>
24−
25 <input
20 <input
26− disabled={ disabled }
21+ disabled={disabled}
27 name={name}
22 name={name}
28 id={id}
23 id={id}
29 type={'checkbox'}
24 type={'checkbox'}
30 className={'none'}
25 className={'none'}
31 checked={state}
26 checked={state}
32− onChange={onChange} />
27+ onChange={onChange}
33−
28+ />
29+
30+ <svg
31+ width="24"
32+ height="24"
33+ viewBox="0 0 24 24"
34+ fill="none"
34− <svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
35+ xmlns="http://www.w3.org/2000/svg">
36+ <path
35− <path d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z' transform='translate(4 11) rotate(-45)' fill='white'/>
37+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
38+ transform="translate(4 11) rotate(-45)"
39+ fill="white"
40+ />
36 </svg>
41 </svg>
37−
38 </label>
42 </label>
39 <div className={`user-select-none ${childContainerClassname} mt-1`}>
43 <div className={`user-select-none ${childContainerClassname} mt-1`}>
40− { children }
44+ {children}
41 </div>
45 </div>
42 </div>
46 </div>
43− )
47+ );
44−}
48+};
45−
46
49
47−export default CheckBox
50+export default CheckBox;
src/bridge/components/CheckboxButton.js+28−35
@@ -1,62 +1,55 @@
1−import React from 'react'
1+import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3−import ProtoButton from './ProtoButton'
3+import ProtoButton from './ProtoButton';
4
4
5const CheckboxButton = props => {
5const CheckboxButton = props => {
6+ const onOff =
7+ props.state === true
8+ ? 'cb-on br-blue bg-blue outline-blue'
9+ : 'bg-transparent br-black outline-blue';
6
10
7− const onOff = props.state === true
8− ? 'cb-on br-blue bg-blue outline-blue'
9− : 'bg-transparent br-black outline-blue';
10−
11− const checkmarkState = props.state === true ? 'o-100' : 'o-0'
11+ const checkmarkState = props.state === true ? 'o-100' : 'o-0';
12−
13− const classes = props.disabled ? 'br-gray-30 bg-gray-10' : onOff
14
12
13+ const classes = props.disabled ? 'br-gray-30 bg-gray-10' : onOff;
15
14
16 return (
15 return (
17 <div
16 <div
18 className={`flex items-center ${props.className}`}
17 className={`flex items-center ${props.className}`}
19− style={{cursor: 'pointer', ...props.style}}
18+ style={{ cursor: 'pointer', ...props.style }}
20 onClick={props.onToggle}>
19 onClick={props.onToggle}>
21−
22 <ProtoButton
20 <ProtoButton
23 // {...getDomProps(props)}
21 // {...getDomProps(props)}
24 {...props}
22 {...props}
25− className={ `s-7 p-0 o-blue mr-4 bs-solid bw-2 ${classes}` }>
23+ className={`s-7 p-0 o-blue mr-4 bs-solid bw-2 ${classes}`}>
26−
27 <svg
24 <svg
28 className={checkmarkState}
25 className={checkmarkState}
29− width='24'
26+ width="24"
30− height='24'
27+ height="24"
31− viewBox='0 0 24 24'
28+ viewBox="0 0 24 24"
32− fill='none'
29+ fill="none"
33− xmlns='http://www.w3.org/2000/svg'>
30+ xmlns="http://www.w3.org/2000/svg">
34 <path
31 <path
35− d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z'
32+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
36− transform='translate(6, 13) rotate(-45)'
33+ transform="translate(6, 13) rotate(-45)"
37− fill='white'/>
34+ fill="white"
35+ />
38 </svg>
36 </svg>
39−
40 </ProtoButton>
37 </ProtoButton>
41
38
42− <div>{ props.label }</div>
39+ <div>{props.label}</div>
43− { props.children }
40+ {props.children}
44−
45 </div>
41 </div>
46− )
42+ );
47−}
43+};
48−
49
44
50CheckboxButton.propTypes = {
45CheckboxButton.propTypes = {
51− state: PropTypes.bool,
46+ state: PropTypes.bool,
52− className: PropTypes.string,
47+ className: PropTypes.string,
53};
48};
54
49
55−
56CheckboxButton.defaultProps = {
50CheckboxButton.defaultProps = {
57− state: false,
51+ state: false,
58− className: '',
52+ className: '',
59};
53};
60
54
61−
62−export default CheckboxButton
55+export default CheckboxButton;
src/bridge/components/Code.js+4−7
@@ -1,16 +1,15 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const Code = ({ className, children, style, measure }) => {
4const Code = ({ className, children, style, measure }) => {
5−
6 // const s = getStyles(styleManifest, measure)
5 // const s = getStyles(styleManifest, measure)
7
6
8 return (
7 return (
9 <pre className={`flex ${className}`} style={style}>
8 <pre className={`flex ${className}`} style={style}>
10− { children }
9+ {children}
11 </pre>
10 </pre>
12− )
11+ );
13−}
12+};
14
13
15// const getStyles = (sm, m) => sm[m];
14// const getStyles = (sm, m) => sm[m];
16//
15//
@@ -26,12 +25,10 @@ Code.propTypes = {
26 style: PropTypes.object,
25 style: PropTypes.object,
27};
26};
28
27
29−
30Code.defaultProps = {
28Code.defaultProps = {
31 className: '',
29 className: '',
32 style: {},
30 style: {},
33 measure: 'md',
31 measure: 'md',
34};
32};
35
33
36−
37export default Code;
34export default Code;
src/bridge/components/Col.js+3−9
@@ -1,17 +1,12 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5−const Col = ({
6− className,
4+const Col = ({ className, children, style }) => {
7− children,
8− style,
9−}) => {
10 return (
5 return (
11 <div className={`col- ${className}`} style={style}>
6 <div className={`col- ${className}`} style={style}>
12− { children }
7+ {children}
13 </div>
8 </div>
14− )
9+ );
15};
10};
16
11
17Col.propTypes = {
12Col.propTypes = {
@@ -25,5 +20,4 @@ Col.defaultProps = {
25 style: {},
20 style: {},
26};
21};
27
22
28−
29export default Col;
23export default Col;
src/bridge/components/Container.js+3−9
@@ -1,17 +1,12 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5−const Container = ({
4+const Container = ({ className, children, style }) => {
6− className,
7− children,
8− style,
9−}) => {
10 return (
5 return (
11 <main className={`container ${className}`} style={style}>
6 <main className={`container ${className}`} style={style}>
12− { children }
7+ {children}
13 </main>
8 </main>
14− )
9+ );
15};
10};
16
11
17Container.propTypes = {
12Container.propTypes = {
@@ -25,5 +20,4 @@ Container.defaultProps = {
25 style: {},
20 style: {},
26};
21};
27
22
28−
29export default Container;
23export default Container;
src/bridge/components/DropdownDivider.js+1−2
@@ -1,12 +1,11 @@
1import React from 'react';
1import React from 'react';
2
2
3−
4const DropdownDivider = props => {
3const DropdownDivider = props => {
5 return (
4 return (
6 <div className={`br-gray-50 bs-solid bw-0-2-0-2 ${props.className}`}>
5 <div className={`br-gray-50 bs-solid bw-0-2-0-2 ${props.className}`}>
7 <div className={`block bs-solid bw-2-0-0-0 br-gray-30 mv-2`} />
6 <div className={`block bs-solid bw-2-0-0-0 br-gray-30 mv-2`} />
8 </div>
7 </div>
9− )
8+ );
10};
9};
11
10
12export default DropdownDivider;
11export default DropdownDivider;
src/bridge/components/DropdownItem.js+2−5
@@ -1,9 +1,7 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5const DropdownItem = props => {
4const DropdownItem = props => {
6−
7 const _disabled = props.disabled
5 const _disabled = props.disabled
8 ? 'gray-50'
6 ? 'gray-50'
9 : 'h-br-blue br-gray-50 h-bg-blue h-white';
7 : 'h-br-blue br-gray-50 h-bg-blue h-white';
@@ -14,9 +12,9 @@ const DropdownItem = props => {
14 style={props.style}
12 style={props.style}
15 className={`h-10 m-0 p-0 ph-2 bg-white flex align-center items-start bs-solid brw-2 blw-2 btw-0 bbw-0 sans ${_disabled} ${props.className}`}
13 className={`h-10 m-0 p-0 ph-2 bg-white flex align-center items-start bs-solid brw-2 blw-2 btw-0 bbw-0 sans ${_disabled} ${props.className}`}
16 onClick={props.onClick}>
14 onClick={props.onClick}>
17− { props.children }
15+ {props.children}
18 </button>
16 </button>
19− )
17+ );
20};
18};
21
19
22DropdownItem.propTypes = {
20DropdownItem.propTypes = {
@@ -27,7 +25,6 @@ DropdownItem.propTypes = {
27 style: PropTypes.object,
25 style: PropTypes.object,
28};
26};
29
27
30−
31DropdownItem.defaultProps = {
28DropdownItem.defaultProps = {
32 className: '',
29 className: '',
33 onClick: () => {},
30 onClick: () => {},
src/bridge/components/FlexCol.js+3−9
@@ -1,17 +1,12 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5−const FlexCol = ({
4+const FlexCol = ({ className, children, style }) => {
6− className,
7− children,
8− style,
9−}) => {
10 return (
5 return (
11 <div className={`flex flex-column flex-1 ${className}`} style={style}>
6 <div className={`flex flex-column flex-1 ${className}`} style={style}>
12− { children }
7+ {children}
13 </div>
8 </div>
14− )
9+ );
15};
10};
16
11
17FlexCol.propTypes = {
12FlexCol.propTypes = {
@@ -25,5 +20,4 @@ FlexCol.defaultProps = {
25 style: {},
20 style: {},
26};
21};
27
22
28−
29export default FlexCol;
23export default FlexCol;
src/bridge/components/FlexRow.js+5−14
@@ -1,26 +1,17 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5−const FlexRow = ({
4+const FlexRow = ({ className, children }) => {
6− className,
7− children,
8−}) => {
9− return (
10− <div className={`flex ${className}`}>
5+ return <div className={`flex ${className}`}>{children}</div>;
11− { children }
12− </div>
13− )
14};
6};
15
7
16FlexRow.propTypes = {
8FlexRow.propTypes = {
17 className: PropTypes.string,
9 className: PropTypes.string,
18 children: PropTypes.node,
10 children: PropTypes.node,
19−}
11+};
20
12
21FlexRow.defaultProps = {
13FlexRow.defaultProps = {
22 className: '',
14 className: '',
23−}
15+};
24−
25
16
26−export default FlexRow
17+export default FlexRow;
src/bridge/components/Footer.js+7−15
@@ -1,23 +1,15 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import { version } from '../../../package.json'
3+import { version } from '../../../package.json';
4
4
5−const Footer = (props) =>
5+const Footer = props => (
6 <footer className={'row h-13 mt-20'}>
6 <footer className={'row h-13 mt-20'}>
7−
8− <div className={'col-md-6 text-mono'}>
7+ <div className={'col-md-6 text-mono'}>{'v' + version}</div>
9− { 'v' + version }
10− </div>
11
8
12 <div className={'col-md-3'}>
9 <div className={'col-md-3'}>
13− <a href='https://github.com/urbit/bridge'>
10+ <a href="https://github.com/urbit/bridge">{'GitHub'}</a>
14− { 'GitHub' }
15− </a>
16 </div>
11 </div>
17−
18 </footer>
12 </footer>
13+);
19
14
20−
21−
22−
23−export default Footer
15+export default Footer;
src/bridge/components/Form.js+3−9
@@ -1,17 +1,12 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5−const Form = ({
4+const Form = ({ className, children, style }) => {
6− className,
7− children,
8− style,
9−}) => {
10 return (
5 return (
11 <form className={`${className}`} style={style}>
6 <form className={`${className}`} style={style}>
12− { children }
7+ {children}
13 </form>
8 </form>
14− )
9+ );
15};
10};
16
11
17Form.propTypes = {
12Form.propTypes = {
@@ -25,5 +20,4 @@ Form.defaultProps = {
25 style: {},
20 style: {},
26};
21};
27
22
28−
29export default Form;
23export default Form;
src/bridge/components/H1.js+7−8
@@ -1,14 +1,15 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const H1 = ({ className, children, style }) => {
4const H1 = ({ className, children, style }) => {
5 return (
5 return (
6+ <h1
6− <h1 className={`mt-12 pt-6 mb-4 text-700 measure-md ${className}`} style={style}>
7+ className={`mt-12 pt-6 mb-4 text-700 measure-md ${className}`}
8+ style={style}>
7− { children }
9+ {children}
8 </h1>
10 </h1>
9− )
11+ );
10−}
12+};
11−
12
13
13H1.propTypes = {
14H1.propTypes = {
14 className: PropTypes.string,
15 className: PropTypes.string,
@@ -16,11 +17,9 @@ H1.propTypes = {
16 style: PropTypes.object,
17 style: PropTypes.object,
17};
18};
18
19
19−
20H1.defaultProps = {
20H1.defaultProps = {
21 className: '',
21 className: '',
22 style: {},
22 style: {},
23};
23};
24
24
25−
26export default H1;
25export default H1;
src/bridge/components/H2.js+4−7
@@ -1,14 +1,13 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const H2 = ({ className, children, style }) => {
4const H2 = ({ className, children, style }) => {
5 return (
5 return (
6 <h2 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
6 <h2 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
8 </h2>
8 </h2>
9− )
9+ );
10−}
10+};
11−
12
11
13H2.propTypes = {
12H2.propTypes = {
14 className: PropTypes.string,
13 className: PropTypes.string,
@@ -16,11 +15,9 @@ H2.propTypes = {
16 style: PropTypes.object,
15 style: PropTypes.object,
17};
16};
18
17
19−
20H2.defaultProps = {
18H2.defaultProps = {
21 className: '',
19 className: '',
22 style: {},
20 style: {},
23};
21};
24
22
25−
26export default H2;
23export default H2;
src/bridge/components/H3.js+4−7
@@ -1,14 +1,13 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const H3 = ({ className, children, style }) => {
4const H3 = ({ className, children, style }) => {
5 return (
5 return (
6 <h3 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
6 <h3 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
8 </h3>
8 </h3>
9− )
9+ );
10−}
10+};
11−
12
11
13H3.propTypes = {
12H3.propTypes = {
14 className: PropTypes.string,
13 className: PropTypes.string,
@@ -16,11 +15,9 @@ H3.propTypes = {
16 style: PropTypes.object,
15 style: PropTypes.object,
17};
16};
18
17
19−
20H3.defaultProps = {
18H3.defaultProps = {
21 className: '',
19 className: '',
22 style: {},
20 style: {},
23};
21};
24
22
25−
26export default H3;
23export default H3;
src/bridge/components/H4.js+4−7
@@ -1,14 +1,13 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const H4 = ({ className, children, style }) => {
4const H4 = ({ className, children, style }) => {
5 return (
5 return (
6 <h4 className={`mt-2 pt-1 mb-2 measure-md ${className}`} style={style}>
6 <h4 className={`mt-2 pt-1 mb-2 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
8 </h4>
8 </h4>
9− )
9+ );
10−}
10+};
11−
12
11
13H4.propTypes = {
12H4.propTypes = {
14 className: PropTypes.string,
13 className: PropTypes.string,
@@ -16,11 +15,9 @@ H4.propTypes = {
16 style: PropTypes.object,
15 style: PropTypes.object,
17};
16};
18
17
19−
20H4.defaultProps = {
18H4.defaultProps = {
21 className: '',
19 className: '',
22 style: {},
20 style: {},
23};
21};
24
22
25−
26export default H4;
23export default H4;
src/bridge/components/Header.js+44−40
@@ -1,54 +1,58 @@
1−import React from 'react'
1+import React from 'react';
2−import { Button, Chevron } from '../components/Base'
2+import { Button, Chevron } from '../components/Base';
3
3
4−import { renderRoute } from '../lib/router'
4+import { renderRoute } from '../lib/router';
5−import { isLast } from '../lib/lib'
5+import { isLast } from '../lib/lib';
6
6
7−const Crumbs = (props) => {
7+const Crumbs = props => {
8− const { routeCrumbs, skipRoute } = props
8+ const { routeCrumbs, skipRoute } = props;
9− const history = routeCrumbs.reverse()
9+ const history = routeCrumbs.reverse();
10
10
11 // FIXME probably more straightforward to render them normally and just
11 // FIXME probably more straightforward to render them normally and just
12 // return the reversed array of renders
12 // return the reversed array of renders
13 const rendered = history.map((route, idx) => {
13 const rendered = history.map((route, idx) => {
14− return (
14+ return (
15− <div className={'flex items-center'} key={ `history-${idx}` }>
15+ <div className={'flex items-center'} key={`history-${idx}`}>
16− <Button
16+ <Button
17− prop-type={'link'}
17+ prop-type={'link'}
18− prop-size={'sm'}
18+ prop-size={'sm'}
19− key={ `history-button-${idx}` }
19+ key={`history-button-${idx}`}
20− onClick={ () => skipRoute(history.size - idx - 1) }>
20+ onClick={() => skipRoute(history.size - idx - 1)}>
21− { renderRoute(props, route) }
21+ {renderRoute(props, route)}
22− </Button>
22+ </Button>
23−
23+
24− {
25− isLast(history.size, idx)
24+ {isLast(history.size, idx) ? (
26− ? <div />
25+ <div />
26+ ) : (
27− : <Chevron className={'h-4 mh-2'} />
27+ <Chevron className={'h-4 mh-2'} />
28− }
28+ )}
29− </div>
29+ </div>
30− )
30+ );
31− })
31+ });
32−
32+
33− return rendered
33+ return rendered;
34−}
34+};
35−
35+
36−const Header = (props) => {
36+const Header = props => {
37 const crumbs = (
37 const crumbs = (
38 <div className={'flex items-center h-10'}>
38 <div className={'flex items-center h-10'}>
39 <Crumbs
39 <Crumbs
40− routeCrumbs={ props.routeCrumbs }
40+ routeCrumbs={props.routeCrumbs}
41− skipRoute={ props.skipRoute }
41+ skipRoute={props.skipRoute}
42− networkType={ props.networkType }
42+ networkType={props.networkType}
43− wallet={ props.wallet }
43+ wallet={props.wallet}
44− pointCursor={ props.pointCursor }
44+ pointCursor={props.pointCursor}
45 />
45 />
46 </div>
46 </div>
47− )
47+ );
48
48
49+ const noCrumbs = (
49− const noCrumbs = <h2 className="mt-9 clickable" onClick={() => props.skipRoute(1)}>Bridge</h2>
50+ <h2 className="mt-9 clickable" onClick={() => props.skipRoute(1)}>
51+ Bridge
52+ </h2>
53+ );
50
54
51− return props.showCrumbs ? crumbs : noCrumbs
55+ return props.showCrumbs ? crumbs : noCrumbs;
52−}
56+};
53
57
54−export default Header
58+export default Header;
src/bridge/components/HorizontalSelector.js+11−17
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4class HorizontalSelector extends React.Component {
4class HorizontalSelector extends React.Component {
@@ -6,7 +6,7 @@ class HorizontalSelector extends React.Component {
6 super(props);
6 super(props);
7
7
8 this.state = {
8 this.state = {
9− option: this.props.options[0].value
9+ option: this.props.options[0].value,
10 };
10 };
11
11
12 this.selectOption = this.selectOption.bind(this);
12 this.selectOption = this.selectOption.bind(this);
@@ -14,44 +14,38 @@ class HorizontalSelector extends React.Component {
14
14
15 selectOption(opt) {
15 selectOption(opt) {
16 this.props.onChange(opt);
16 this.props.onChange(opt);
17− this.setState({option: opt});
17+ this.setState({ option: opt });
18 }
18 }
19
19
20 getOptionElems() {
20 getOptionElems() {
21 return this.props.options.map(opt => {
21 return this.props.options.map(opt => {
22− let active = (opt.value === this.state.option)
22+ let active = opt.value === this.state.option ? 'btn-primary active' : '';
23− ? 'btn-primary active'
24− : '';
25 return (
23 return (
26 <button
24 <button
27 className={`btn ${active} h-10 ph-4 flex sans ${this.props.className}`}
25 className={`btn ${active} h-10 ph-4 flex sans ${this.props.className}`}
28 value={opt.value}
26 value={opt.value}
29 onClick={() => this.selectOption(opt.value)}>
27 onClick={() => this.selectOption(opt.value)}>
30− { opt.title }
28+ {opt.title}
31 </button>
29 </button>
32− )
30+ );
33− })
31+ });
34 }
32 }
35
33
36 render() {
34 render() {
37 const optionElems = this.getOptionElems();
35 const optionElems = this.getOptionElems();
38
36
39− return (
40− <div className='btn-group'>
37+ return <div className="btn-group">{optionElems}</div>;
41− { optionElems }
42− </div>
43− )
44 }
38 }
45}
39}
46
40
47HorizontalSelector.propTypes = {
41HorizontalSelector.propTypes = {
48 className: PropTypes.string,
42 className: PropTypes.string,
49− options: PropTypes.array
43+ options: PropTypes.array,
50};
44};
51
45
52HorizontalSelector.defaultProps = {
46HorizontalSelector.defaultProps = {
53 className: '',
47 className: '',
54− options: []
48+ options: [],
55};
49};
56
50
57−export default HorizontalSelector
51+export default HorizontalSelector;
src/bridge/components/InnerLabel.js+5−6
@@ -1,23 +1,22 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5const InnerLabel = ({ children, className, style }) => {
4const InnerLabel = ({ children, className, style }) => {
6 return (
5 return (
6+ <div
7− <div className={`abs tl-0 pt-2 pl-4 fs-3 text-500 sans ${className}`} style={style}>
7+ className={`abs tl-0 pt-2 pl-4 fs-3 text-500 sans ${className}`}
8+ style={style}>
8− { children }
9+ {children}
9 </div>
10 </div>
10− )
11+ );
11};
12};
12
13
13−
14InnerLabel.propTypes = {
14InnerLabel.propTypes = {
15 className: PropTypes.string,
15 className: PropTypes.string,
16 children: PropTypes.node,
16 children: PropTypes.node,
17 style: PropTypes.object,
17 style: PropTypes.object,
18};
18};
19
19
20−
21InnerLabel.defaultProps = {
20InnerLabel.defaultProps = {
22 className: '',
21 className: '',
23 style: {},
22 style: {},
src/bridge/components/InnerLabelDropdown.js+36−35
@@ -1,65 +1,63 @@
1−import React from 'react'
1+import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−import { DropdownItem, DropdownDivider } from '../components/Base'
4+import { DropdownItem, DropdownDivider } from '../components/Base';
5
5
6class InnerLabelDropdown extends React.Component {
6class InnerLabelDropdown extends React.Component {
7 constructor(props) {
7 constructor(props) {
8− super(props)
8+ super(props);
9
9
10 this.state = {
10 this.state = {
11− isOpen: false
11+ isOpen: false,
12− }
12+ };
13
13
14− this.toggle = this.toggle.bind(this)
14+ this.toggle = this.toggle.bind(this);
15− this.close = this.close.bind(this)
15+ this.close = this.close.bind(this);
16 }
16 }
17
17
18 toggle() {
18 toggle() {
19 this.setState({
19 this.setState({
20− isOpen: !this.state.isOpen
20+ isOpen: !this.state.isOpen,
21− })
21+ });
22 }
22 }
23
23
24 close() {
24 close() {
25 this.setState({
25 this.setState({
26− isOpen: false
26+ isOpen: false,
27− })
27+ });
28 }
28 }
29
29
30 selectOption(opt) {
30 selectOption(opt) {
31− this.close()
31+ this.close();
32− this.props.handleUpdate(opt)
32+ this.props.handleUpdate(opt);
33 }
33 }
34
34
35 getOptionElems() {
35 getOptionElems() {
36 return this.props.options.map((opt, i) => {
36 return this.props.options.map((opt, i) => {
37− if (opt.type === "divider") {
37+ if (opt.type === 'divider') {
38− return <DropdownDivider key={`divider-${i}`} />
38+ return <DropdownDivider key={`divider-${i}`} />;
39 } else {
39 } else {
40 return (
40 return (
41+ <DropdownItem
42+ key={opt.title}
41− <DropdownItem key={opt.title} onClick={() => this.selectOption(opt.value)}>
43+ onClick={() => this.selectOption(opt.value)}>
42− { opt.title }
44+ {opt.title}
43 </DropdownItem>
45 </DropdownItem>
44− )
46+ );
45 }
47 }
46− })
48+ });
47 }
49 }
48
50
49 render() {
51 render() {
50− const { props, state } = this
52+ const { props, state } = this;
51
53
52− const optionElems = this.getOptionElems()
54+ const optionElems = this.getOptionElems();
53
55
54− const menuVisibility = state.isOpen === true
56+ const menuVisibility = state.isOpen === true ? 'visible' : 'hidden';
55− ? 'visible'
56− : 'hidden';
57
57
58− const selectedClasses = state.isOpen === true
58+ const selectedClasses = state.isOpen === true ? 'br-blue' : 'br-gray-50';
59− ? 'br-blue'
60− : 'br-gray-50';
61
59
62− const fullWidthClass = props.fullWidth ? 'full-width' : ''
60+ const fullWidthClass = props.fullWidth ? 'full-width' : '';
63
61
64 return (
62 return (
65 <div className={`rel table ${fullWidthClass} ${props.className}`}>
63 <div className={`rel table ${fullWidthClass} ${props.className}`}>
@@ -73,24 +71,27 @@ class InnerLabelDropdown extends React.Component {
73 onClick={this.toggle}
71 onClick={this.toggle}
74 disabled={props.disabled}>
72 disabled={props.disabled}>
75 <div className={'flex'}>
73 <div className={'flex'}>
74+ <div
76− <div className={`ph-4 flex align-center b-2 br-0 b-solid h-10 ${selectedClasses} ${fullWidthClass}`}>
75+ className={`ph-4 flex align-center b-2 br-0 b-solid h-10 ${selectedClasses} ${fullWidthClass}`}>
77 <div className={'text-400 mr-2'}>{props.title}</div>
76 <div className={'text-400 mr-2'}>{props.title}</div>
78 <div className={'text-600'}>{props.currentSelectionTitle}</div>
77 <div className={'text-600'}>{props.currentSelectionTitle}</div>
79 </div>
78 </div>
80− <div className={'white bg-blue s-10 flex-center-all text-sm'}>{'▼'}</div>
79+ <div className={'white bg-blue s-10 flex-center-all text-sm'}>
80+ {'▼'}
81+ </div>
81 </div>
82 </div>
82 </button>
83 </button>
83
84
85+ <div
84− <div className={`abs flex-column bg-white t-12 l-0 w-100 z-1 ${menuVisibility}`}>
86+ className={`abs flex-column bg-white t-12 l-0 w-100 z-1 ${menuVisibility}`}>
85 <div className={'pv-1 b-solid b-gray-50 b-2 bb-0'} />
87 <div className={'pv-1 b-solid b-gray-50 b-2 bb-0'} />
86
88
87− { optionElems }
89+ {optionElems}
88
90
89 <div className={'pv-1 b-solid b-gray-50 b-2 bt-0'} />
91 <div className={'pv-1 b-solid b-gray-50 b-2 bt-0'} />
90 </div>
92 </div>
91−
92 </div>
93 </div>
93− )
94+ );
94 }
95 }
95}
96}
96
97
@@ -111,4 +112,4 @@ InnerLabelDropdown.defaultProps = {
111 currentSelectionTitle: '',
112 currentSelectionTitle: '',
112};
113};
113
114
114−export default InnerLabelDropdown
115+export default InnerLabelDropdown;
src/bridge/components/Input.js+30−36
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
3import ProtoInput from './ProtoInput';
3import ProtoInput from './ProtoInput';
4
4
5const Input = props => {
5const Input = props => {
6−
7 const _disabled = props.disabled === true ? 'br-gray-30' : '';
6 const _disabled = props.disabled === true ? 'br-gray-30' : '';
8
7
9 // This enables all children of this component to share the state of this
8 // This enables all children of this component to share the state of this
@@ -15,14 +14,13 @@ const Input = props => {
15 focussed: props.focussed,
14 focussed: props.focussed,
16 beenFocussed: props.beenFocussed,
15 beenFocussed: props.beenFocussed,
17 error: props.error,
16 error: props.error,
18− }
17+ };
19− return React.cloneElement(child, propsToPass)
18+ return React.cloneElement(child, propsToPass);
20 });
19 });
21
20
22 const className = props.advanced
21 const className = props.advanced
23 ? `${props.valClassname} ${props.className}`
22 ? `${props.valClassname} ${props.className}`
24− : `br-gray-50 f-br-blue outline-blue ${props.className}`
23+ : `br-gray-50 f-br-blue outline-blue ${props.className}`;
25−
26
24
27 return (
25 return (
28 <div className={'relative'}>
26 <div className={'relative'}>
@@ -30,44 +28,40 @@ const Input = props => {
30 {...props}
28 {...props}
31 // prop-focussed={props.focussed.toString()}
29 // prop-focussed={props.focussed.toString()}
32 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
30 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
33− onChange={e => props.onChange(e.target.value)} />
31+ onChange={e => props.onChange(e.target.value)}
34− {
32+ />
35− props.displayFailure === true && props.advanced === true
33+ {props.displayFailure === true && props.advanced === true ? (
36− ? <div className={'yellow text-600 h-6 mt-1 fs-3'}>{props.error}</div>
34+ <div className={'yellow text-600 h-6 mt-1 fs-3'}>{props.error}</div>
35+ ) : (
37− : <div/>
36+ <div />
38− }
37+ )}
39− {
40− childrenWithProps
38+ {childrenWithProps}
41− }
42 </div>
39 </div>
43 );
40 );
44−}
41+};
45−
46
42
47Input.propTypes = {
43Input.propTypes = {
48− focussed: PropTypes.bool,
44+ focussed: PropTypes.bool,
49− beenFocussed: PropTypes.bool,
45+ beenFocussed: PropTypes.bool,
50− hasSubmitted: PropTypes.bool,
46+ hasSubmitted: PropTypes.bool,
51− displayPass: PropTypes.bool,
47+ displayPass: PropTypes.bool,
52− displayFailure: PropTypes.bool,
48+ displayFailure: PropTypes.bool,
53− advanced: PropTypes.bool,
49+ advanced: PropTypes.bool,
54− error: PropTypes.string,
50+ error: PropTypes.string,
55− valClassname: PropTypes.string,
51+ valClassname: PropTypes.string,
56− size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
52+ size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
57};
53};
58
54
59−
60Input.defaultProps = {
55Input.defaultProps = {
61− focussed: false,
56+ focussed: false,
62− beenFocussed: false,
57+ beenFocussed: false,
63− hasSubmitted: false,
58+ hasSubmitted: false,
64− displayPass: false,
59+ displayPass: false,
65− displayFailure: false,
60+ displayFailure: false,
66− advanced: false,
61+ advanced: false,
67− error: '',
62+ error: '',
68− size: 'm',
63+ size: 'm',
69− format: 'normal',
64+ format: 'normal',
70};
65};
71
66
72−
73export default Input;
67export default Input;
src/bridge/components/InputCaption.js+5−6
@@ -1,23 +1,22 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5const InputCaption = ({ children, className, style }) => {
4const InputCaption = ({ children, className, style }) => {
6 return (
5 return (
6+ <div
7− <div className={`mt-8 mb-1 text-35 text-400 sans ${className}`} style={style}>
7+ className={`mt-8 mb-1 text-35 text-400 sans ${className}`}
8+ style={style}>
8− { children }
9+ {children}
9 </div>
10 </div>
10− )
11+ );
11};
12};
12
13
13−
14InputCaption.propTypes = {
14InputCaption.propTypes = {
15 className: PropTypes.string,
15 className: PropTypes.string,
16 children: PropTypes.node,
16 children: PropTypes.node,
17 style: PropTypes.object,
17 style: PropTypes.object,
18};
18};
19
19
20−
21InputCaption.defaultProps = {
20InputCaption.defaultProps = {
22 className: '',
21 className: '',
23 style: {},
22 style: {},
src/bridge/components/Label.js+2−5
@@ -1,23 +1,20 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−
5const Label = ({ children, className }) => {
4const Label = ({ children, className }) => {
6 return (
5 return (
7 <div className={`mb-1 mt-8 fs-4 text-600 sans ${className}`}>
6 <div className={`mb-1 mt-8 fs-4 text-600 sans ${className}`}>
8− { children }
7+ {children}
9 </div>
8 </div>
10− )
9+ );
11};
10};
12
11
13−
14Label.propTypes = {
12Label.propTypes = {
15 className: PropTypes.string,
13 className: PropTypes.string,
16 children: PropTypes.node,
14 children: PropTypes.node,
17 style: PropTypes.object,
15 style: PropTypes.object,
18};
16};
19
17
20−
21Label.defaultProps = {
18Label.defaultProps = {
22 className: '',
19 className: '',
23 style: {},
20 style: {},
src/bridge/components/OldHeader.js+38−41
@@ -1,52 +1,49 @@
1−import React from 'react'
1+import React from 'react';
2−import { Button, Chevron } from '../components/Base'
2+import { Button, Chevron } from '../components/Base';
3
3
4−import { renderRoute } from '../lib/router'
4+import { renderRoute } from '../lib/router';
5−import { isLast } from '../lib/lib'
5+import { isLast } from '../lib/lib';
6
6
7−const Crumbs = (props) => {
7+const Crumbs = props => {
8− const { routeCrumbs, skipRoute } = props
8+ const { routeCrumbs, skipRoute } = props;
9− const history = routeCrumbs.reverse()
9+ const history = routeCrumbs.reverse();
10
10
11 // FIXME probably more straightforward to render them normally and just
11 // FIXME probably more straightforward to render them normally and just
12 // return the reversed array of renders
12 // return the reversed array of renders
13 const rendered = history.map((route, idx) => {
13 const rendered = history.map((route, idx) => {
14−
15− return (
14+ return (
16− <div className={'flex items-center'} key={ `history-${idx}` }>
15+ <div className={'flex items-center'} key={`history-${idx}`}>
17−
18− <Button
16+ <Button
19− size={'s'}
17+ size={'s'}
20− type={'link'}
18+ type={'link'}
21− color={'black'}
19+ color={'black'}
22− key={ `history-button-${idx}` }
20+ key={`history-button-${idx}`}
23− onClick={ () => skipRoute(history.size - idx - 1) }
21+ onClick={() => skipRoute(history.size - idx - 1)}>
24− >
25− { `${renderRoute(props, route)}` }
22+ {`${renderRoute(props, route)}`}
26− </Button>
23+ </Button>
27−
24+
28− {
29− isLast(history.size, idx)
25+ {isLast(history.size, idx) ? (
30− ? <div />
26+ <div />
27+ ) : (
31− : <Chevron className={'h-4 mh-2'} />
28+ <Chevron className={'h-4 mh-2'} />
32− }
29+ )}
33−
34− </div>
30+ </div>
35− )
31+ );
36− })
32+ });
37−
33+
38− return rendered
34+ return rendered;
39−}
35+};
40−
36+
41−const Header = (props) =>
37+const Header = props => (
42 <div className={'flex items-center h-8 pt-4'}>
38 <div className={'flex items-center h-8 pt-4'}>
43 <Crumbs
39 <Crumbs
44− routeCrumbs={ props.routeCrumbs }
40+ routeCrumbs={props.routeCrumbs}
45− skipRoute={ props.skipRoute }
41+ skipRoute={props.skipRoute}
46− networkType={ props.networkType }
42+ networkType={props.networkType}
47− wallet={ props.wallet }
43+ wallet={props.wallet}
48− pointCursor={ props.pointCursor }
44+ pointCursor={props.pointCursor}
49 />
45 />
50 </div>
46 </div>
47+);
51
48
52−export default Header
49+export default Header;
src/bridge/components/OldLink.js+9−16
@@ -14,12 +14,9 @@ const Link = ({
14 color,
14 color,
15 type,
15 type,
16}) => {
16}) => {
17−
18−
19−
20− const _color = disabled ? 'disabled' : color
17+ const _color = disabled ? 'disabled' : color;
21− const _s = getStyles(styleManifest, type, _color, size)
18+ const _s = getStyles(styleManifest, type, _color, size);
22− const _disabled = disabled ? 'disabled': ''
19+ const _disabled = disabled ? 'disabled' : '';
23
20
24 return (
21 return (
25 <a
22 <a
@@ -29,12 +26,11 @@ const Link = ({
29 disabled={disabled}
26 disabled={disabled}
30 autoFocus={autoFocus}
27 autoFocus={autoFocus}
31 href={href}
28 href={href}
32− target={target}
29+ target={target}>
33− >
34− { children }
30+ {children}
35 </a>
31 </a>
36− )
32+ );
37−}
33+};
38
34
39const getStyles = (m, t, c, s) => `${m[t][c]} ${m[t][s]} ${m[t]['all']}`;
35const getStyles = (m, t, c, s) => `${m[t][c]} ${m[t][s]} ${m[t]['all']}`;
40
36
@@ -98,9 +94,8 @@ const styleManifest = {
98 l: 'h-13 ph-6 fs-5',
94 l: 'h-13 ph-6 fs-5',
99 // Applied to all block buttons
95 // Applied to all block buttons
100 all: 'white b-2 br-solid',
96 all: 'white b-2 br-solid',
101− }
97+ },
102−}
98+};
103−
104
99
105Link.propTypes = {
100Link.propTypes = {
106 disabled: PropTypes.bool,
101 disabled: PropTypes.bool,
@@ -111,7 +106,6 @@ Link.propTypes = {
111 style: PropTypes.object,
106 style: PropTypes.object,
112};
107};
113
108
114−
115Link.defaultProps = {
109Link.defaultProps = {
116 className: '',
110 className: '',
117 autoFocus: false,
111 autoFocus: false,
@@ -123,5 +117,4 @@ Link.defaultProps = {
123 style: {},
117 style: {},
124};
118};
125
119
126−
127export default Link;
120export default Link;
src/bridge/components/OldTextArea.js+8−15
@@ -30,7 +30,6 @@ const TextArea = ({
30 displayFailure,
30 displayFailure,
31 error,
31 error,
32}) => {
32}) => {
33−
34 const _disabled = disabled === true ? 'br-gray-30' : '';
33 const _disabled = disabled === true ? 'br-gray-30' : '';
35
34
36 const s = getStyles(styleManifest, format, size);
35 const s = getStyles(styleManifest, format, size);
@@ -58,15 +57,12 @@ const TextArea = ({
58 maxLength={maxLength}
57 maxLength={maxLength}
59 readOnly={readOnly}
58 readOnly={readOnly}
60 />
59 />
61− {
62− childrenWithProps
60+ {childrenWithProps}
63− }
64− {
65− displayFailure === true && isValidated === true
61+ {displayFailure === true && isValidated === true ? (
66− ? <div className={'yellow text-600 h-6 mt-1'}>{error}</div>
62+ <div className={'yellow text-600 h-6 mt-1'}>{error}</div>
63+ ) : (
67− : <div/>
64+ <div />
68− }
65+ )}
69−
70 </div>
66 </div>
71 );
67 );
72};
68};
@@ -78,9 +74,8 @@ const styleManifest = {
78 l: 'h-80 fs-7 ph-4',
74 l: 'h-80 fs-7 ph-4',
79 m: 'h-40 fs-35 ph-4',
75 m: 'h-40 fs-35 ph-4',
80 s: 'h-24 fs-35 ph-1',
76 s: 'h-24 fs-35 ph-1',
81− }
77+ },
82−}
78+};
83−
84
79
85TextArea.propTypes = {
80TextArea.propTypes = {
86 disabled: PropTypes.bool,
81 disabled: PropTypes.bool,
@@ -105,7 +100,6 @@ TextArea.propTypes = {
105 validationClassnames: PropTypes.string,
100 validationClassnames: PropTypes.string,
106};
101};
107
102
108−
109TextArea.defaultProps = {
103TextArea.defaultProps = {
110 disabled: false,
104 disabled: false,
111 onChange: () => {},
105 onChange: () => {},
@@ -125,5 +119,4 @@ TextArea.defaultProps = {
125 validationClassnames: 'br-gray-50 f-br-blue outline-blue',
119 validationClassnames: 'br-gray-50 f-br-blue outline-blue',
126};
120};
127
121
128−
129export default TextArea;
122export default TextArea;
src/bridge/components/P.js+6−9
@@ -1,16 +1,15 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const P = ({ className, children, style, measure }) => {
4const P = ({ className, children, style, measure }) => {
5−
6− const s = getStyles(styleManifest, measure)
5+ const s = getStyles(styleManifest, measure);
7
6
8 return (
7 return (
9 <p className={`mb-7 ${s} ${className}`} style={style}>
8 <p className={`mb-7 ${s} ${className}`} style={style}>
10− { children }
9+ {children}
11 </p>
10 </p>
12− )
11+ );
13−}
12+};
14
13
15const getStyles = (sm, m) => sm[m];
14const getStyles = (sm, m) => sm[m];
16
15
@@ -18,7 +17,7 @@ const styleManifest = {
18 sm: 'measure-sm',
17 sm: 'measure-sm',
19 md: 'measure-md',
18 md: 'measure-md',
20 lg: 'measure-lg',
19 lg: 'measure-lg',
21−}
20+};
22
21
23P.propTypes = {
22P.propTypes = {
24 className: PropTypes.string,
23 className: PropTypes.string,
@@ -26,12 +25,10 @@ P.propTypes = {
26 style: PropTypes.object,
25 style: PropTypes.object,
27};
26};
28
27
29−
30P.defaultProps = {
28P.defaultProps = {
31 className: '',
29 className: '',
32 style: {},
30 style: {},
33 measure: 'md',
31 measure: 'md',
34};
32};
35
33
36−
37export default P;
34export default P;
src/bridge/components/Passport.js+125−129
@@ -1,240 +1,236 @@
1−import React from 'react'
1+import React from 'react';
2−import { pour } from 'sigil-js'
2+import { pour } from 'sigil-js';
3−import * as ob from 'urbit-ob'
3+import * as ob from 'urbit-ob';
4−import ReactSVGComponents from './ReactSVGComponents'
4+import ReactSVGComponents from './ReactSVGComponents';
5−import PropTypes from 'prop-types'
5+import PropTypes from 'prop-types';
6−import PaperCollateralRenderer from 'PaperCollateralRenderer'
6+import PaperCollateralRenderer from 'PaperCollateralRenderer';
7−import { Button } from './Base'
7+import { Button } from './Base';
8
8
9−import { WALLET_STATES } from '../lib/invite'
9+import { WALLET_STATES } from '../lib/invite';
10−import { downloadWallet } from '../lib/invite'
10+import { downloadWallet } from '../lib/invite';
11
11
12class Passport extends React.Component {
12class Passport extends React.Component {
13−
14 constructor(props) {
13 constructor(props) {
15− super(props)
14+ super(props);
16
15
17 this.state = {
16 this.state = {
18− paper: null
17+ paper: null,
19− }
18+ };
20
19
21− this.masterCensor = this.generateCensor()
20+ this.masterCensor = this.generateCensor();
22− this.managementCensor = this.generateCensor()
21+ this.managementCensor = this.generateCensor();
23
22
24− this.download = this.download.bind(this)
23+ this.download = this.download.bind(this);
25 }
24 }
26
25
27 download() {
26 download() {
28− downloadWallet(this.state.paper)
27+ downloadWallet(this.state.paper);
29− this.props.pushWalletState(WALLET_STATES.DOWNLOADED)
28+ this.props.pushWalletState(WALLET_STATES.DOWNLOADED);
30 }
29 }
31
30
32 generateCensor() {
31 generateCensor() {
33 let blocks = [];
32 let blocks = [];
34
33
35− (new Uint32Array(86)).forEach((_, i) => {
34+ new Uint32Array(86).forEach((_, i) => {
36− let rand = Math.round(Math.random())
35+ let rand = Math.round(Math.random());
37− let filled = (rand || i === 0 || i === 43)
36+ let filled = rand || i === 0 || i === 43;
38
37
39− let className = filled ? "passport-block bg-gray-30" : "passport-block"
38+ let className = filled ? 'passport-block bg-gray-30' : 'passport-block';
40
39
41− blocks.push(<div key={i} data-key={i}className={className}></div>)
40+ blocks.push(<div key={i} data-key={i} className={className}></div>);
42− })
41+ });
43
42
44− return (
45− <div className="flex flex-wrap passport-censor">
43+ return <div className="flex flex-wrap passport-censor">{blocks}</div>;
46− {blocks}
47− </div>
48− )
49 }
44 }
50
45
51 getCurrentDate() {
46 getCurrentDate() {
52− let date = new Date()
47+ let date = new Date();
53− const fil = n => n >= 10 ? n : `0${n}`
48+ const fil = n => (n >= 10 ? n : `0${n}`);
49+ let fmtDate =
54− let fmtDate = `${date.getUTCFullYear()}.` +
50+ `${date.getUTCFullYear()}.` +
55−   `${(date.getUTCMonth() + 1)}.` +
51+ `${date.getUTCMonth() + 1}.` +
56−   `${fil(date.getUTCDate())}`
52+ `${fil(date.getUTCDate())}`;
57−
53+
58− return fmtDate
54+ return fmtDate;
59 }
55 }
60
56
61 getWalletProgress() {
57 getWalletProgress() {
62− let { walletStates } = this.props
58+ let { walletStates } = this.props;
63
59
64− if ( !walletStates.includes(WALLET_STATES.UNLOCKING) ) return null
60+ if (!walletStates.includes(WALLET_STATES.UNLOCKING)) return null;
65− if ( walletStates.includes(WALLET_STATES.TRANSACTIONS) ) return null
61+ if (walletStates.includes(WALLET_STATES.TRANSACTIONS)) return null;
66
62
67 let progressTimes = {
63 let progressTimes = {
68− [WALLET_STATES.UNLOCKING]: {ms: "2813", pct: "44%"},
64+ [WALLET_STATES.UNLOCKING]: { ms: '2813', pct: '44%' },
69− [WALLET_STATES.GENERATING]: {ms: "2433", pct: "38%"},
65+ [WALLET_STATES.GENERATING]: { ms: '2433', pct: '38%' },
70− [WALLET_STATES.RENDERING]: {ms: "986", pct: "15%"}
66+ [WALLET_STATES.RENDERING]: { ms: '986', pct: '15%' },
71− }
67+ };
72
68
73 const progressBars = Object.keys(progressTimes).map(state => {
69 const progressBars = Object.keys(progressTimes).map(state => {
74− let progressIndex = walletStates.indexOf(state)
70+ let progressIndex = walletStates.indexOf(state);
71+ let progressDone =
75− let progressDone = progressIndex !== -1 && progressIndex < walletStates.length -1
72+ progressIndex !== -1 && progressIndex < walletStates.length - 1;
76
73
77 let progressStyle = {
74 let progressStyle = {
78− width: progressTimes[state].pct
75+ width: progressTimes[state].pct,
79− }
76+ };
80
77
81 return (
78 return (
82 <div className="passport-wallet-progress" style={progressStyle}>
79 <div className="passport-wallet-progress" style={progressStyle}>
83− {progressDone &&
80+ {progressDone && (
84 <div className="passport-wallet-progress-fill"></div>
81 <div className="passport-wallet-progress-fill"></div>
85− }
82+ )}
86 </div>
83 </div>
87− )
84+ );
88− })
85+ });
89
86
90− return (
91− <div className="flex space-between mt-3">
87+ return <div className="flex space-between mt-3">{progressBars}</div>;
92− {progressBars}
93− </div>
94− )
95 }
88 }
96
89
97 getWalletStates() {
90 getWalletStates() {
98− let { walletStates } = this.props
91+ let { walletStates } = this.props;
99
92
100− if ( walletStates.includes(WALLET_STATES.TRANSACTIONS) ) return null
93+ if (walletStates.includes(WALLET_STATES.TRANSACTIONS)) return null;
101
94
102 let stateElems = walletStates.map(state => {
95 let stateElems = walletStates.map(state => {
103− if (state === WALLET_STATES.DOWNLOADED) return null
96+ if (state === WALLET_STATES.DOWNLOADED) return null;
104
97
105− const lastIndex = walletStates.indexOf(state) === walletStates.length - 1
98+ const lastIndex = walletStates.indexOf(state) === walletStates.length - 1;
106− const checkmark = lastIndex
99+ const checkmark = lastIndex ? null : (
107− ? null
108− : <div className="text-700 text-sm lh-6 green-dark">✓</div>
100+ <div className="text-700 text-sm lh-6 green-dark">✓</div>
101+ );
109
102
110 return (
103 return (
111 <div className="flex justify-between">
104 <div className="flex justify-between">
112 <div className="text-mono text-sm lh-6 green-dark">{state}</div>
105 <div className="text-mono text-sm lh-6 green-dark">{state}</div>
113− { checkmark }
106+ {checkmark}
114 </div>
107 </div>
115− )
108+ );
116− })
109+ });
117
110
118− return (
119− <div className="mt-3">
111+ return <div className="mt-3">{stateElems}</div>;
120− {stateElems}
121− </div>
122− )
123 }
112 }
124
113
125 render() {
114 render() {
126 let pointVal = this.props.point.matchWith({
115 let pointVal = this.props.point.matchWith({
127 Just: p => p.value,
116 Just: p => p.value,
128− Nothing: () => ''
117+ Nothing: () => '',
129− })
118+ });
130
119
131− let patp = pointVal === '' ? '' : ob.patp(pointVal)
120+ let patp = pointVal === '' ? '' : ob.patp(pointVal);
132
121
133 let wallet = this.props.wallet.matchWith({
122 let wallet = this.props.wallet.matchWith({
134 Just: w => w.value,
123 Just: w => w.value,
135− Nothing: () => null
124+ Nothing: () => null,
136− })
125+ });
137
126
138 let ownershipAddress = this.props.wallet.matchWith({
127 let ownershipAddress = this.props.wallet.matchWith({
139 Just: w => w.value.ownership.keys.address,
128 Just: w => w.value.ownership.keys.address,
140− Nothing: () => null
129+ Nothing: () => null,
141− })
130+ });
142
131
143− const sigil = patp !== ''
132+ const sigil =
133+ patp !== '' ? (
144− ? pour({
134+ pour({
145 patp: patp,
135 patp: patp,
146 renderer: ReactSVGComponents,
136 renderer: ReactSVGComponents,
147− size: 128
137+ size: 128,
148 })
138 })
139+ ) : (
149− : <div className="passport-sigil-blank"></div>
140+ <div className="passport-sigil-blank"></div>
150−
141+ );
142+
151− let currentDate = this.getCurrentDate()
143+ let currentDate = this.getCurrentDate();
152− let walletStates = this.getWalletStates()
144+ let walletStates = this.getWalletStates();
153− let walletProgress = this.getWalletProgress()
145+ let walletProgress = this.getWalletProgress();
146+ let paperReady =
147+ this.state.paper &&
154− let paperReady = this.state.paper && this.props.walletStates.includes(WALLET_STATES.PAPER_READY)
148+ this.props.walletStates.includes(WALLET_STATES.PAPER_READY);
155− let downloadColor = paperReady ? 'green' : 'black'
149+ let downloadColor = paperReady ? 'green' : 'black';
156
150
157 let downloadBtn = (
151 let downloadBtn = (
158 <Button
152 <Button
159− disabled={ !paperReady }
153+ disabled={!paperReady}
160− prop-size={ 'lg wide' }
154+ prop-size={'lg wide'}
161− prop-color={ downloadColor }
155+ prop-color={downloadColor}
162− onClick={ this.download }
156+ onClick={this.download}>
163− >
164− { 'Download' }
157+ {'Download'}
165 </Button>
158 </Button>
166− )
159+ );
167−
160+
168− let paperCollateral = wallet
161+ let paperCollateral = wallet ? (
169− ? (
170− <PaperCollateralRenderer
162+ <PaperCollateralRenderer
171− wallet={{[pointVal]: wallet}}
163+ wallet={{ [pointVal]: wallet }}
172− className={'extremely-hidden'}
164+ className={'extremely-hidden'}
173− callback={data => {
165+ callback={data => {
174− this.setState({paper: data})
166+ this.setState({ paper: data });
175− this.props.pushWalletState(WALLET_STATES.PAPER_READY)
167+ this.props.pushWalletState(WALLET_STATES.PAPER_READY);
176− }}
168+ }}
177− mode={'REGISTRATION'} />
169+ mode={'REGISTRATION'}
170+ />
178− )
171+ ) : (
179− : <div />
172+ <div />
173+ );
180
174
181 let walletAddresses = (
175 let walletAddresses = (
182 <div className="mt-8">
176 <div className="mt-8">
183 <div className="passport-label text-mono">Ethereum Address</div>
177 <div className="passport-label text-mono">Ethereum Address</div>
178+ <div className="passport-value">
184− <div className="passport-value">{ownershipAddress || 'Ungenerated'}</div>
179+ {ownershipAddress || 'Ungenerated'}
180+ </div>
185
181
186 <div className="passport-label gray-50 mt-3">Master Ticket</div>
182 <div className="passport-label gray-50 mt-3">Master Ticket</div>
187− <div className="passport-value passport-censor overflow-hidden">{this.masterCensor}</div>
183+ <div className="passport-value passport-censor overflow-hidden">
184+ {this.masterCensor}
185+ </div>
188
186
189 <div className="passport-label gray-50 mt-5">Management Seed</div>
187 <div className="passport-label gray-50 mt-5">Management Seed</div>
190− <div className="passport-value passport-censor overflow-hidden">{this.managementCensor}</div>
188+ <div className="passport-value passport-censor overflow-hidden">
189+ {this.managementCensor}
190+ </div>
191 </div>
191 </div>
192− )
192+ );
193
193
194 let walletMetadata = (
194 let walletMetadata = (
195 <div className="flex">
195 <div className="flex">
196− <div className="passport-sigil">
196+ <div className="passport-sigil">{sigil}</div>
197− {sigil}
198− </div>
199 <div className="passport-metadata ml-4">
197 <div className="passport-metadata ml-4">
200 <div className="passport-label text-mono">Point</div>
198 <div className="passport-label text-mono">Point</div>
201 <div className="text-mono">{patp || '-'}</div>
199 <div className="text-mono">{patp || '-'}</div>
202
200
203 <div className="passport-label gray-50 mt-6">Created on</div>
201 <div className="passport-label gray-50 mt-6">Created on</div>
204− <div className="passport-value">{patp ? currentDate : '-' }</div>
202+ <div className="passport-value">{patp ? currentDate : '-'}</div>
205
203
206 <div className="passport-label gray-50 mt-2">Invited by</div>
204 <div className="passport-label gray-50 mt-2">Invited by</div>
207 <div className="passport-value">{this.props.invitedBy || '-'}</div>
205 <div className="passport-value">{this.props.invitedBy || '-'}</div>
208 </div>
206 </div>
209 </div>
207 </div>
210− )
208+ );
211
209
212 return (
210 return (
213 <div>
211 <div>
214 <div className="passport-border">
212 <div className="passport-border">
215 <div className={`passport ${paperReady && 'passport-filled'}`}>
213 <div className={`passport ${paperReady && 'passport-filled'}`}>
216− { walletMetadata}
214+ {walletMetadata}
217− { walletAddresses }
215+ {walletAddresses}
218− { paperCollateral }
216+ {paperCollateral}
219−
220 </div>
217 </div>
221− { downloadBtn }
218+ {downloadBtn}
222 </div>
219 </div>
223
220
224− { walletProgress }
221+ {walletProgress}
225− { walletStates }
222+ {walletStates}
226−
227 </div>
223 </div>
228− )
224+ );
229 }
225 }
230}
226}
231
227
232Passport.propTypes = {
228Passport.propTypes = {
233− wallet: PropTypes.object
229+ wallet: PropTypes.object,
234};
230};
235
231
236Passport.defaultProps = {
232Passport.defaultProps = {
237− wallet: null
233+ wallet: null,
238};
234};
239
235
240−export default Passport
236+export default Passport;
src/bridge/components/PointList.js+24−26
@@ -1,30 +1,28 @@
1−import React from 'react'
1+import React from 'react';
2−import * as lodash from 'lodash'
2+import * as lodash from 'lodash';
3−import { Row } from './Base'
3+import { Row } from './Base';
4−import RenderedPoint from './RenderedPoint'
4+import RenderedPoint from './RenderedPoint';
5
5
6const PointList = props => {
6const PointList = props => {
7− const { setPointCursor, routeHandler, points, loading } = props
7+ const { setPointCursor, routeHandler, points, loading } = props;
8− const chunks = lodash.chunk(points, 3)
8+ const chunks = lodash.chunk(points, 3);
9
9
10− return (
11− points.length === 0
10+ return points.length === 0 ? (
12− ? <p>{loading ? 'Loading...' : 'No points to display' }</p>
11+ <p>{loading ? 'Loading...' : 'No points to display'}</p>
12+ ) : (
13− : lodash.map(chunks, (chunk, idx) =>
13+ lodash.map(chunks, (chunk, idx) => (
14− <Row key={ `render-row-${idx}` }>
14+ <Row key={`render-row-${idx}`}>
15− {
16− lodash.map(chunk, point =>
15+ {lodash.map(chunk, point => (
17− <RenderedPoint
16+ <RenderedPoint
18− key={ point }
17+ key={point}
19− setPointCursor={ setPointCursor }
18+ setPointCursor={setPointCursor}
20− routeHandler={ routeHandler }
19+ routeHandler={routeHandler}
21− point={ point }
20+ point={point}
22− />
21+ />
23− )
22+ ))}
24− }
25− </Row>
23+ </Row>
26− )
24+ ))
27− )
25+ );
28−}
26+};
29
27
30−export default PointList
28+export default PointList;
src/bridge/components/ProtoAnchor.js+15−20
@@ -2,31 +2,26 @@ import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4const ProtoAnchor = props => {
4const ProtoAnchor = props => {
5− return (
6− <a {...props}>
7− { props.children }
5+ return <a {...props}>{props.children}</a>;
8− </a>
9− )
10−}
6+};
11
7
12ProtoAnchor.propTypes = {
8ProtoAnchor.propTypes = {
13− className: PropTypes.string,
9+ className: PropTypes.string,
14− children: PropTypes.node,
10+ children: PropTypes.node,
15− autoFocus: PropTypes.bool,
11+ autoFocus: PropTypes.bool,
16− style: PropTypes.object,
12+ style: PropTypes.object,
17− href: PropTypes.string,
13+ href: PropTypes.string,
18− rel: PropTypes.string,
14+ rel: PropTypes.string,
19− target: PropTypes.oneOf(['_self','_blank','_parent','_top']),
15+ target: PropTypes.oneOf(['_self', '_blank', '_parent', '_top']),
20};
16};
21
17
22ProtoAnchor.defaultProps = {
18ProtoAnchor.defaultProps = {
23− className: '',
19+ className: '',
24− autoFocus: false,
20+ autoFocus: false,
25− style: {},
21+ style: {},
26− href: '',
22+ href: '',
27− rel: '',
23+ rel: '',
28− target: '_self',
24+ target: '_self',
29};
25};
30
26
31−
32export default ProtoAnchor;
27export default ProtoAnchor;
src/bridge/components/ProtoButton.js+16−17
@@ -12,29 +12,28 @@ const ProtoButton = props => {
12 onClick={props.onClick}
12 onClick={props.onClick}
13 disabled={props.disabled}
13 disabled={props.disabled}
14 autoFocus={props.autoFocus}>
14 autoFocus={props.autoFocus}>
15− { props.children }
15+ {props.children}
16 </button>
16 </button>
17− )
17+ );
18−}
18+};
19
19
20ProtoButton.propTypes = {
20ProtoButton.propTypes = {
21− disabled: PropTypes.bool,
21+ disabled: PropTypes.bool,
22− onClick: PropTypes.func,
22+ onClick: PropTypes.func,
23− className: PropTypes.string,
23+ className: PropTypes.string,
24− children: PropTypes.node,
24+ children: PropTypes.node,
25− autofocus: PropTypes.bool,
25+ autofocus: PropTypes.bool,
26− style: PropTypes.object,
26+ style: PropTypes.object,
27− type: PropTypes.oneOf(['button', 'submit', 'reset'])
27+ type: PropTypes.oneOf(['button', 'submit', 'reset']),
28};
28};
29
29
30ProtoButton.defaultProps = {
30ProtoButton.defaultProps = {
31− className: '',
31+ className: '',
32− autoFocus: false,
32+ autoFocus: false,
33− onClick: () => {},
33+ onClick: () => {},
34− disabled: false,
34+ disabled: false,
35− style: {},
35+ style: {},
36− type: 'button',
36+ type: 'button',
37};
37};
38
38
39−
40export default ProtoButton;
39export default ProtoButton;
src/bridge/components/ProtoInput.js+30−32
@@ -1,7 +1,7 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−import { getDomProps } from '../lib/base'
4+import { getDomProps } from '../lib/base';
5
5
6const ProtoInput = props => {
6const ProtoInput = props => {
7 return (
7 return (
@@ -26,43 +26,41 @@ const ProtoInput = props => {
26 autoFocus={props.autoFocus}
26 autoFocus={props.autoFocus}
27 readOnly={props.readOnly}
27 readOnly={props.readOnly}
28 maxLength={props.maxLength}
28 maxLength={props.maxLength}
29− autoComplete={props.autoComplete} />
29+ autoComplete={props.autoComplete}
30+ />
30 );
31 );
31−}
32+};
32−
33
33
34ProtoInput.propTypes = {
34ProtoInput.propTypes = {
35− disabled: PropTypes.bool,
35+ disabled: PropTypes.bool,
36− onChange: PropTypes.func,
36+ onChange: PropTypes.func,
37− onBlur: PropTypes.func,
37+ onBlur: PropTypes.func,
38− onFocus: PropTypes.func,
38+ onFocus: PropTypes.func,
39− onKeyPress: PropTypes.func,
39+ onKeyPress: PropTypes.func,
40− className: PropTypes.string,
40+ className: PropTypes.string,
41− style: PropTypes.object,
41+ style: PropTypes.object,
42− children: PropTypes.node,
42+ children: PropTypes.node,
43− autoFocus: PropTypes.bool,
43+ autoFocus: PropTypes.bool,
44− readOnly: PropTypes.bool,
44+ readOnly: PropTypes.bool,
45− name: PropTypes.string,
45+ name: PropTypes.string,
46− value: PropTypes.string,
46+ value: PropTypes.string,
47− autoComplete: PropTypes.string,
47+ autoComplete: PropTypes.string,
48− type: PropTypes.string,
48+ type: PropTypes.string,
49};
49};
50
50
51−
52ProtoInput.defaultProps = {
51ProtoInput.defaultProps = {
53− disabled: false,
52+ disabled: false,
54− autoFocus: false,
53+ autoFocus: false,
55− onChange: () => {},
54+ onChange: () => {},
56− onBlur: () => {},
55+ onBlur: () => {},
57− onFocus: () => {},
56+ onFocus: () => {},
58− onKeyPress: () => {},
57+ onKeyPress: () => {},
59− style: {},
58+ style: {},
60− className: '',
59+ className: '',
61− value: '',
60+ value: '',
62− name: '',
61+ name: '',
63− autoComplete: 'off',
62+ autoComplete: 'off',
64− type: 'input',
63+ type: 'input',
65};
64};
66
65
67−
68export default ProtoInput;
66export default ProtoInput;
src/bridge/components/ProtoTextarea.js+28−30
@@ -1,7 +1,7 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−import { getDomProps } from '../lib/base'
4+import { getDomProps } from '../lib/base';
5
5
6const ProtoTextarea = props => {
6const ProtoTextarea = props => {
7 return (
7 return (
@@ -24,41 +24,39 @@ const ProtoTextarea = props => {
24 autoFocus={props.autoFocus}
24 autoFocus={props.autoFocus}
25 readOnly={props.readOnly}
25 readOnly={props.readOnly}
26 maxLength={props.maxLength}
26 maxLength={props.maxLength}
27− autoComplete={props.autoComplete} />
27+ autoComplete={props.autoComplete}
28+ />
28 );
29 );
29−}
30+};
30−
31
31
32ProtoTextarea.propTypes = {
32ProtoTextarea.propTypes = {
33− disabled: PropTypes.bool,
33+ disabled: PropTypes.bool,
34− onChange: PropTypes.func,
34+ onChange: PropTypes.func,
35− onBlur: PropTypes.func,
35+ onBlur: PropTypes.func,
36− onFocus: PropTypes.func,
36+ onFocus: PropTypes.func,
37− onKeyPress: PropTypes.func,
37+ onKeyPress: PropTypes.func,
38− className: PropTypes.string,
38+ className: PropTypes.string,
39− style: PropTypes.object,
39+ style: PropTypes.object,
40− children: PropTypes.node,
40+ children: PropTypes.node,
41− autoFocus: PropTypes.bool,
41+ autoFocus: PropTypes.bool,
42− readOnly: PropTypes.bool,
42+ readOnly: PropTypes.bool,
43− name: PropTypes.string,
43+ name: PropTypes.string,
44− value: PropTypes.string,
44+ value: PropTypes.string,
45− autoComplete: PropTypes.string,
45+ autoComplete: PropTypes.string,
46};
46};
47
47
48−
49ProtoTextarea.defaultProps = {
48ProtoTextarea.defaultProps = {
50− disabled: false,
49+ disabled: false,
51− autoFocus: false,
50+ autoFocus: false,
52− onChange: () => {},
51+ onChange: () => {},
53− onBlur: () => {},
52+ onBlur: () => {},
54− onFocus: () => {},
53+ onFocus: () => {},
55− onKeyPress: () => {},
54+ onKeyPress: () => {},
56− style: {},
55+ style: {},
57− className: '',
56+ className: '',
58− value: '',
57+ value: '',
59− name: '',
58+ name: '',
60− autoComplete: 'off',
59+ autoComplete: 'off',
61};
60};
62
61
63−
64export default ProtoTextarea;
62export default ProtoTextarea;
src/bridge/components/ReactSVGComponents.js+42−22
@@ -1,63 +1,83 @@
1−import React from 'react'
1+import React from 'react';
2−import { get, map } from 'lodash'
2+import { get, map } from 'lodash';
3
3
4const ReactSVGComponents = {
4const ReactSVGComponents = {
5 svg: (p, key) => {
5 svg: (p, key) => {
6 return (
6 return (
7+ <svg
8+ {...p.attr}
9+ version={'1.1'}
7− <svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'} key={key}>
10+ xmlns={'http://www.w3.org/2000/svg'}
11+ key={key}>
12+ {map(get(p, 'children', []), (child, idx) =>
8− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
13+ ReactSVGComponents[child.tag](child, idx)
14+ )}
9 </svg>
15 </svg>
10− )
16+ );
11 },
17 },
12 circle: (p, key) => {
18 circle: (p, key) => {
13 return (
19 return (
14 <circle {...p.attr} key={key}>
20 <circle {...p.attr} key={key}>
21+ {map(get(p, 'children', []), (child, idx) =>
15− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
22+ ReactSVGComponents[child.tag](child, idx)
23+ )}
16 </circle>
24 </circle>
17− )
25+ );
18 },
26 },
19 rect: (p, key) => {
27 rect: (p, key) => {
20 return (
28 return (
21 <rect {...p.attr} key={key}>
29 <rect {...p.attr} key={key}>
30+ {map(get(p, 'children', []), (child, idx) =>
22− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
31+ ReactSVGComponents[child.tag](child, idx)
32+ )}
23 </rect>
33 </rect>
24− )
34+ );
25 },
35 },
26 path: (p, key) => {
36 path: (p, key) => {
27 return (
37 return (
28 <path {...p.attr} key={key}>
38 <path {...p.attr} key={key}>
39+ {map(get(p, 'children', []), (child, idx) =>
29− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
40+ ReactSVGComponents[child.tag](child, idx)
41+ )}
30 </path>
42 </path>
31− )
43+ );
32 },
44 },
33 g: (p, key) => {
45 g: (p, key) => {
34 return (
46 return (
35 <g {...p.attr} key={key}>
47 <g {...p.attr} key={key}>
48+ {map(get(p, 'children', []), (child, idx) =>
36− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
49+ ReactSVGComponents[child.tag](child, idx)
50+ )}
37 </g>
51 </g>
38− )
52+ );
39 },
53 },
40 polygon: (p, key) => {
54 polygon: (p, key) => {
41 return (
55 return (
42 <polygon {...p.attr} key={key}>
56 <polygon {...p.attr} key={key}>
57+ {map(get(p, 'children', []), (child, idx) =>
43− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
58+ ReactSVGComponents[child.tag](child, idx)
59+ )}
44 </polygon>
60 </polygon>
45− )
61+ );
46 },
62 },
47 line: (p, key) => {
63 line: (p, key) => {
48 return (
64 return (
49 <line {...p.attr} key={key}>
65 <line {...p.attr} key={key}>
66+ {map(get(p, 'children', []), (child, idx) =>
50− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
67+ ReactSVGComponents[child.tag](child, idx)
68+ )}
51 </line>
69 </line>
52− )
70+ );
53 },
71 },
54 polyline: (p, key) => {
72 polyline: (p, key) => {
55 return (
73 return (
56 <polyline {...p.attr} key={key}>
74 <polyline {...p.attr} key={key}>
75+ {map(get(p, 'children', []), (child, idx) =>
57− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
76+ ReactSVGComponents[child.tag](child, idx)
77+ )}
58 </polyline>
78 </polyline>
59− )
79+ );
60− }
80+ },
61−}
81+};
62
82
63−export default ReactSVGComponents
83+export default ReactSVGComponents;
src/bridge/components/RenderedPoint.js+36−35
@@ -1,56 +1,57 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Col } from './Base'
3+import { Col } from './Base';
4−import { Button } from './Base'
4+import { Button } from './Base';
5−import { pour } from 'sigil-js'
5+import { pour } from 'sigil-js';
6−import * as ob from 'urbit-ob'
6+import * as ob from 'urbit-ob';
7
7
8−import { ROUTE_NAMES } from '../lib/router'
8+import { ROUTE_NAMES } from '../lib/router';
9−import ReactSVGComponents from './ReactSVGComponents'
9+import ReactSVGComponents from './ReactSVGComponents';
10
10
11−const RenderedPoint = (props) => {
11+const RenderedPoint = props => {
12− const { setPointCursor, routeHandler, point } = props
12+ const { setPointCursor, routeHandler, point } = props;
13
13
14− const name = ob.patp(point)
14+ const name = ob.patp(point);
15 const sigil = pour({
15 const sigil = pour({
16 patp: name,
16 patp: name,
17 renderer: ReactSVGComponents,
17 renderer: ReactSVGComponents,
18− size: 128
18+ size: 128,
19− })
19+ });
20
20
21 return (
21 return (
22− <Col className={'col-md-3 mb-8'} key={ `rendered-${point}` }>
22+ <Col className={'col-md-3 mb-8'} key={`rendered-${point}`}>
23+ <div
23− <div key={ `rendered-sigil-${point}` }
24+ key={`rendered-sigil-${point}`}
24 className="clickable"
25 className="clickable"
25− onClick={ () => {
26+ onClick={() => {
26− setPointCursor(Maybe.Just(point))
27+ setPointCursor(Maybe.Just(point));
27− routeHandler(ROUTE_NAMES.SHIP)
28+ routeHandler(ROUTE_NAMES.SHIP);
28 }}>
29 }}>
29− { sigil }
30+ {sigil}
30 </div>
31 </div>
32+ <div
31− <div key={ `rendered-name-${point}` }
33+ key={`rendered-name-${point}`}
32 className="clickable"
34 className="clickable"
33− onClick={ () => {
35+ onClick={() => {
34− setPointCursor(Maybe.Just(point))
36+ setPointCursor(Maybe.Just(point));
35− routeHandler(ROUTE_NAMES.SHIP)
37+ routeHandler(ROUTE_NAMES.SHIP);
36 }}>
38 }}>
37− <code>{ name }</code>
39+ <code>{name}</code>
38 </div>
40 </div>
39− <div key={ `rendered-deets-${point}` }>
41+ <div key={`rendered-deets-${point}`}>
40 <Button
42 <Button
41− key={ `rendered-deets-button-${point}` }
43+ key={`rendered-deets-button-${point}`}
42 prop-type={'link'}
44 prop-type={'link'}
43 prop-size={'sm'}
45 prop-size={'sm'}
44− onClick={ () => {
46+ onClick={() => {
45− setPointCursor(Maybe.Just(point))
47+ setPointCursor(Maybe.Just(point));
46− routeHandler(ROUTE_NAMES.SHIP)
48+ routeHandler(ROUTE_NAMES.SHIP);
47− }}
49+ }}>
48− >
49− { 'Details →' }
50+ {'Details →'}
50 </Button>
51 </Button>
51 </div>
52 </div>
52 </Col>
53 </Col>
53− )
54+ );
54−}
55+};
55
56
56−export default RenderedPoint
57+export default RenderedPoint;
src/bridge/components/Row.js+4−9
@@ -1,16 +1,12 @@
1import React from 'react';
1import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4−const Row = ({
5− className,
4+const Row = ({ className, children, style }) => {
6− children,
7− style,
8−}) => {
9 return (
5 return (
10 <div className={`row ${className}`} style={style}>
6 <div className={`row ${className}`} style={style}>
11− { children }
7+ {children}
12 </div>
8 </div>
13− )
9+ );
14};
10};
15
11
16Row.propTypes = {
12Row.propTypes = {
@@ -24,5 +20,4 @@ Row.defaultProps = {
24 style: {},
20 style: {},
25};
21};
26
22
27−
28−export default Row
23+export default Row;
src/bridge/components/RowCol.js+5−10
@@ -1,18 +1,15 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4−import { Row, Col } from './Base'
4+import { Row, Col } from './Base';
5
5
6const RowCol = ({ className, children, style }) => {
6const RowCol = ({ className, children, style }) => {
7 return (
7 return (
8 <Row className={`${className}`} style={style}>
8 <Row className={`${className}`} style={style}>
9− <Col>
10− { children }
9+ <Col>{children}</Col>
11− </Col>
12 </Row>
10 </Row>
13− )
11+ );
14−}
12+};
15−
16
13
17RowCol.propTypes = {
14RowCol.propTypes = {
18 className: PropTypes.string,
15 className: PropTypes.string,
@@ -20,11 +17,9 @@ RowCol.propTypes = {
20 style: PropTypes.object,
17 style: PropTypes.object,
21};
18};
22
19
23−
24RowCol.defaultProps = {
20RowCol.defaultProps = {
25 className: '',
21 className: '',
26 style: {},
22 style: {},
27};
23};
28
24
29−
30export default RowCol;
25export default RowCol;
src/bridge/components/ShowBlockie.js+32−43
@@ -1,81 +1,70 @@
1−import React from 'react'
1+import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3−import makeBlockie from 'ethereum-blockies-base64'
3+import makeBlockie from 'ethereum-blockies-base64';
4−import Button from './Button'
4+import Button from './Button';
5−import { invert } from '../lib/state'
5+import { invert } from '../lib/state';
6
6
7−const isValidAddress = (address) => /^0x[0-9a-fA-F]{40}$/.test(address);
7+const isValidAddress = address => /^0x[0-9a-fA-F]{40}$/.test(address);
8
8
9class ShowBlockie extends React.Component {
9class ShowBlockie extends React.Component {
10−
11 constructor(props) {
10 constructor(props) {
12− super(props)
11+ super(props);
13
12
14 this.state = {
13 this.state = {
15− open: false
14+ open: false,
16− }
15+ };
17
16
18− this.toggle = this.toggle.bind(this)
17+ this.toggle = this.toggle.bind(this);
19 // this.close = this.close.bind(this)
18 // this.close = this.close.bind(this)
20−
21 }
19 }
22
20
23− toggle = () => this.setState(invert('open', this.state))
21+ toggle = () => this.setState(invert('open', this.state));
24
22
25 render() {
23 render() {
26− const { props, state } = this
24+ const { props, state } = this;
27
25
28− const display = state.open
26+ const display = state.open ? '' : 'super-hidden';
29− ? ''
30− : 'super-hidden'
31
27
32− const isValid = isValidAddress(props.address)
28+ const isValid = isValidAddress(props.address);
33
29
34 return (
30 return (
35 <div className={`flex flex-column items-start ${props.className}`}>
31 <div className={`flex flex-column items-start ${props.className}`}>
36 <Button
32 <Button
37 onClick={this.toggle}
33 onClick={this.toggle}
38 disabled={!isValid}
34 disabled={!isValid}
39− prop-type='link'
35+ prop-type="link"
40− prop-size='sm'>
36+ prop-size="sm">
41− { state.open
42− ? 'Hide Blockie'
43− : 'Display Blockie'
37+ {state.open ? 'Hide Blockie' : 'Display Blockie'}
44− }
45− </Button>
38+ </Button>
46− {
47− !isValid
39+ {!isValid ? (
48− ? <div/>
40+ <div />
41+ ) : (
49− : <img
42+ <img
50− className={`${display} mt-2`}
43+ className={`${display} mt-2`}
51− alt={ props.address }
44+ alt={props.address}
52− src={ makeBlockie(props.address) }
45+ src={makeBlockie(props.address)}
53− style={{
46+ style={{
54− width: props.size,
47+ width: props.size,
55− height: props.size,
48+ height: props.size,
56− }} />
49+ }}
57− }
50+ />
58−
51+ )}
59 </div>
52 </div>
60− )
53+ );
61 }
54 }
62}
55}
63
56
64−
65ShowBlockie.propTypes = {
57ShowBlockie.propTypes = {
66 className: PropTypes.string,
58 className: PropTypes.string,
67 children: PropTypes.node,
59 children: PropTypes.node,
68 style: PropTypes.object,
60 style: PropTypes.object,
69};
61};
70
62
71−
72ShowBlockie.defaultProps = {
63ShowBlockie.defaultProps = {
73 className: '',
64 className: '',
74 style: {},
65 style: {},
75 size: 64,
66 size: 64,
76 address: '',
67 address: '',
77−
78};
68};
79
69
80−
81export default ShowBlockie;
70export default ShowBlockie;
src/bridge/components/Spacing.js+1−8
@@ -2,17 +2,11 @@ import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3import { isUndefined } from 'lodash';
3import { isUndefined } from 'lodash';
4
4
5−
6const Spacing = ({ className, children, h, w }) => {
5const Spacing = ({ className, children, h, w }) => {
7−
8 const h_cn = isUndefined(h) ? '' : `mt-${h}`;
6 const h_cn = isUndefined(h) ? '' : `mt-${h}`;
9 const w_cn = isUndefined(w) ? '' : `ml-${w}`;
7 const w_cn = isUndefined(w) ? '' : `ml-${w}`;
10
8
11− return (
12− <div className={`${h_cn} ${w_cn} ${className}`}>
9+ return <div className={`${h_cn} ${w_cn} ${className}`}>{children}</div>;
13− { children }
14− </div>
15− );
16};
10};
17
11
18Spacing.propTypes = {
12Spacing.propTypes = {
@@ -28,5 +22,4 @@ Spacing.defaultProps = {
28 w: 0,
22 w: 0,
29};
23};
30
24
31−
32export default Spacing;
25export default Spacing;
src/bridge/components/StatelessTransaction.js+297−281
@@ -1,17 +1,16 @@
1−import { Nothing, Just } from 'folktale/maybe'
1+import { Nothing, Just } from 'folktale/maybe';
2−import { Ok } from 'folktale/result'
2+import { Ok } from 'folktale/result';
3−import React from 'react'
3+import React from 'react';
4−import * as need from '../lib/need'
4+import * as need from '../lib/need';
5
5
6−import { Code, H3 } from './Base'
6+import { Code, H3 } from './Base';
7−import { Button } from './Base'
7+import { Button } from './Base';
8−import { CheckboxButton, Input, InnerLabel,
8+import { CheckboxButton, Input, InnerLabel, InnerLabelDropdown } from './Base';
9− InnerLabelDropdown } from './Base'
10−import { Warning } from '../components/Base'
9+import { Warning } from '../components/Base';
11
10
12−import { BRIDGE_ERROR } from '../lib/error'
11+import { BRIDGE_ERROR } from '../lib/error';
13−import { ROUTE_NAMES } from '../lib/router'
12+import { ROUTE_NAMES } from '../lib/router';
14−import * as tank from '../lib/tank'
13+import * as tank from '../lib/tank';
15
14
16import {
15import {
17 sendSignedTransaction,
16 sendSignedTransaction,
@@ -19,20 +18,19 @@ import {
19 toWei,
18 toWei,
20 hexify,
19 hexify,
21 renderSignedTx,
20 renderSignedTx,
22− signTransaction
21+ signTransaction,
23− } from '../lib/txn'
22+} from '../lib/txn';
24
23
25const SUBMISSION_STATES = {
24const SUBMISSION_STATES = {
26 PROMPT: 'Send transaction',
25 PROMPT: 'Send transaction',
27 PREPARING: 'Preparing...',
26 PREPARING: 'Preparing...',
28 FUNDING: 'Finding transaction funding...',
27 FUNDING: 'Finding transaction funding...',
29− SENDING: 'Sending transaction...'
28+ SENDING: 'Sending transaction...',
30};
29};
31
30
32class StatelessTransaction extends React.Component {
31class StatelessTransaction extends React.Component {
33−
34 constructor(props) {
32 constructor(props) {
35− super(props)
33+ super(props);
36
34
37 this.state = {
35 this.state = {
38 gasPrice: '5',
36 gasPrice: '5',
@@ -45,37 +43,37 @@ class StatelessTransaction extends React.Component {
45 stx: Nothing(),
43 stx: Nothing(),
46 txn: Nothing(),
44 txn: Nothing(),
47 txStatus: SUBMISSION_STATES.PROMPT,
45 txStatus: SUBMISSION_STATES.PROMPT,
48− txError: Nothing()
46+ txError: Nothing(),
49− }
47+ };
50−
48+
51− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
49+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
52− this.setUserApproval = this.setUserApproval.bind(this)
50+ this.setUserApproval = this.setUserApproval.bind(this);
53− this.setTxn = this.setTxn.bind(this)
51+ this.setTxn = this.setTxn.bind(this);
54− this.setStx = this.setStx.bind(this)
52+ this.setStx = this.setStx.bind(this);
55− this.sendTxn = this.sendTxn.bind(this)
53+ this.sendTxn = this.sendTxn.bind(this);
56− this.setNonce = this.setNonce.bind(this)
54+ this.setNonce = this.setNonce.bind(this);
57− this.setChainId = this.setChainId.bind(this)
55+ this.setChainId = this.setChainId.bind(this);
58− this.setGasPrice = this.setGasPrice.bind(this)
56+ this.setGasPrice = this.setGasPrice.bind(this);
59− this.setGasLimit = this.setGasLimit.bind(this)
57+ this.setGasLimit = this.setGasLimit.bind(this);
60− this.rangeChange = this.rangeChange.bind(this)
58+ this.rangeChange = this.rangeChange.bind(this);
61− this.toggleGasDetails = this.toggleGasDetails.bind(this)
59+ this.toggleGasDetails = this.toggleGasDetails.bind(this);
62− this.handleChainUpdate = this.handleChainUpdate.bind(this)
60+ this.handleChainUpdate = this.handleChainUpdate.bind(this);
63 }
61 }
64
62
65 componentDidMount() {
63 componentDidMount() {
66− const { props } = this
64+ const { props } = this;
67
65
68 const addr = need.address(props);
66 const addr = need.address(props);
69
67
70 props.web3.matchWith({
68 props.web3.matchWith({
71 Nothing: () => {},
69 Nothing: () => {},
72− Just: (w3) => {
70+ Just: w3 => {
73− const validWeb3 = w3.value
71+ const validWeb3 = w3.value;
74
72
75 const getTxMetadata = [
73 const getTxMetadata = [
76 validWeb3.eth.getTransactionCount(addr),
74 validWeb3.eth.getTransactionCount(addr),
77 validWeb3.eth.net.getId(),
75 validWeb3.eth.net.getId(),
78− validWeb3.eth.getGasPrice()
76+ validWeb3.eth.getGasPrice(),
79 ];
77 ];
80
78
81 Promise.all(getTxMetadata).then(r => {
79 Promise.all(getTxMetadata).then(r => {
@@ -85,54 +83,53 @@ class StatelessTransaction extends React.Component {
85 gasPrice: fromWei(r[2], 'gwei'),
83 gasPrice: fromWei(r[2], 'gwei'),
86 };
84 };
87
85
88− this.setState({...txMetadata})
86+ this.setState({ ...txMetadata });
89−
90− })
87+ });
91− }
88+ },
92 });
89 });
93 }
90 }
94
91
95 handleChainUpdate(chainId) {
92 handleChainUpdate(chainId) {
96− if (chainId === "custom") {
93+ if (chainId === 'custom') {
97 this.setState({
94 this.setState({
98− customChain: true
95+ customChain: true,
99− })
96+ });
100 } else {
97 } else {
101 this.setState({
98 this.setState({
102 customChain: false,
99 customChain: false,
103− chainId
100+ chainId,
104− })
101+ });
105 }
102 }
106
103
107− this.clearStx()
104+ this.clearStx();
108 }
105 }
109
106
110− setUserApproval(){
107+ setUserApproval() {
111− const {state} = this
108+ const { state } = this;
112− this.setState({ userApproval: !state.userApproval })
109+ this.setState({ userApproval: !state.userApproval });
113 }
110 }
114
111
115 toggleGasDetails() {
112 toggleGasDetails() {
116 this.setState({
113 this.setState({
117− showGasDetails: !this.state.showGasDetails
114+ showGasDetails: !this.state.showGasDetails,
118− })
115+ });
119 }
116 }
120
117
121− setStx(stx){
118+ setStx(stx) {
122 this.setState({
119 this.setState({
123 stx,
120 stx,
124 userApproval: false,
121 userApproval: false,
125− })
122+ });
126 }
123 }
127
124
128− setTxn(txn){
125+ setTxn(txn) {
129− this.setState({ txn })
126+ this.setState({ txn });
130 }
127 }
131
128
132 createUnsignedTxn() {
129 createUnsignedTxn() {
133− const txn = this.props.createUnsignedTxn()
130+ const txn = this.props.createUnsignedTxn();
134
131
135− this.setState({ txn })
132+ this.setState({ txn });
136 }
133 }
137
134
138 clearTxn() {
135 clearTxn() {
@@ -140,38 +137,38 @@ class StatelessTransaction extends React.Component {
140 userApproval: false,
137 userApproval: false,
141 txn: Nothing(),
138 txn: Nothing(),
142 stx: Nothing(),
139 stx: Nothing(),
143− })
140+ });
144 }
141 }
145
142
146− setNonce(nonce){
143+ setNonce(nonce) {
147− this.setState({ nonce })
144+ this.setState({ nonce });
148− this.clearStx()
145+ this.clearStx();
149 }
146 }
150
147
151− setChainId(chainId){
148+ setChainId(chainId) {
152− this.setState({ chainId })
149+ this.setState({ chainId });
153− this.clearStx()
150+ this.clearStx();
154 }
151 }
155
152
156 setGasPrice(gasPrice) {
153 setGasPrice(gasPrice) {
157− this.setState({ gasPrice })
154+ this.setState({ gasPrice });
158− this.clearStx()
155+ this.clearStx();
159 }
156 }
160
157
161 rangeChange(e) {
158 rangeChange(e) {
162 this.setGasPrice(e.target.value);
159 this.setGasPrice(e.target.value);
163 }
160 }
164
161
165− setGasLimit(gasLimit){
162+ setGasLimit(gasLimit) {
166− this.setState({ gasLimit })
163+ this.setState({ gasLimit });
167− this.clearStx()
164+ this.clearStx();
168 }
165 }
169
166
170 clearStx() {
167 clearStx() {
171 this.setState({
168 this.setState({
172 userApproval: false,
169 userApproval: false,
173 stx: Nothing(),
170 stx: Nothing(),
174− })
171+ });
175 }
172 }
176
173
177 async sendTxn() {
174 async sendTxn() {
@@ -182,7 +179,7 @@ class StatelessTransaction extends React.Component {
182 Just: tx => tx.value,
179 Just: tx => tx.value,
183 Nothing: () => {
180 Nothing: () => {
184 throw BRIDGE_ERROR.MISSING_TXN;
181 throw BRIDGE_ERROR.MISSING_TXN;
185− }
182+ },
186 });
183 });
187
184
188 if (props.beforeSend) {
185 if (props.beforeSend) {
@@ -194,7 +191,7 @@ class StatelessTransaction extends React.Component {
194 console.log('beforeSend error', e);
191 console.log('beforeSend error', e);
195 this.setState({
192 this.setState({
196 txStatus: SUBMISSION_STATES.PROMPT,
193 txStatus: SUBMISSION_STATES.PROMPT,
197− txError: Just('Something went wrong!')
194+ txError: Just('Something went wrong!'),
198 });
195 });
199 return;
196 return;
200 }
197 }
@@ -203,11 +200,11 @@ class StatelessTransaction extends React.Component {
203 this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
200 this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
204
201
205 const rawTx = hexify(stx.serialize());
202 const rawTx = hexify(stx.serialize());
206− const cost = (state.gasLimit * toWei(state.gasPrice, 'gwei'));
203+ const cost = state.gasLimit * toWei(state.gasPrice, 'gwei');
207
204
208 const address = need.address(props);
205 const address = need.address(props);
209 let balance = await web3.eth.getBalance(address);
206 let balance = await web3.eth.getBalance(address);
210− let hasBalance = (balance >= cost);
207+ let hasBalance = balance >= cost;
211
208
212 let usedTank = false;
209 let usedTank = false;
213 // if we need to, try and fund the transaction
210 // if we need to, try and fund the transaction
@@ -222,11 +219,10 @@ class StatelessTransaction extends React.Component {
222 txStatus: SUBMISSION_STATES.PROMPT,
219 txStatus: SUBMISSION_STATES.PROMPT,
223 txError: Just(`Insufficient funds.
220 txError: Just(`Insufficient funds.
224 Address ${address} needs at least ${fromWei(cost.toString())} ETH,
221 Address ${address} needs at least ${fromWei(cost.toString())} ETH,
225− currently has ${fromWei(balance.toString())} ETH.`
222+ currently has ${fromWei(balance.toString())} ETH.`),
226− )
227 });
223 });
228
224
229− // if we have the balance, proceed with submission
225+ // if we have the balance, proceed with submission
230 } else {
226 } else {
231 this.setState({ txStatus: SUBMISSION_STATES.SENDING });
227 this.setState({ txStatus: SUBMISSION_STATES.SENDING });
232
228
@@ -236,23 +232,23 @@ class StatelessTransaction extends React.Component {
236 usedTank,
232 usedTank,
237 props.setTxnConfirmations
233 props.setTxnConfirmations
238 )
234 )
239− .then(txHash => {
235+ .then(txHash => {
240− props.onSent(Just(Ok(txHash)), state.stx.value);
236+ props.onSent(Just(Ok(txHash)), state.stx.value);
241− props.popRoute();
237+ props.popRoute();
242−
238+
243− let routeData = {};
239+ let routeData = {};
244− if (props.networkSeed) {
240+ if (props.networkSeed) {
245− props.setNetworkSeedCache(props.networkSeed);
241+ props.setNetworkSeedCache(props.networkSeed);
246− routeData.promptKeyfile = true;
242+ routeData.promptKeyfile = true;
247− }
243+ }
248− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, routeData);
244+ props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, routeData);
249− })
245+ })
250− .catch(err => {
246+ .catch(err => {
251− this.setState({
247+ this.setState({
252− txStatus: SUBMISSION_STATES.PROMPT,
248+ txStatus: SUBMISSION_STATES.PROMPT,
253− txError: Just(err)
249+ txError: Just(err),
250+ });
254 });
251 });
255− });
256 }
252 }
257 }
253 }
258
254
@@ -261,101 +257,122 @@ class StatelessTransaction extends React.Component {
261 // returns true if the gas tank was used, false otherwise
257 // returns true if the gas tank was used, false otherwise
262 ensureFundsFor(web3, address, cost, signedTxs) {
258 ensureFundsFor(web3, address, cost, signedTxs) {
263 return new Promise((resolve, reject) => {
259 return new Promise((resolve, reject) => {
260+ tank
264− tank.ensureFundsFor(web3, null, address, cost, signedTxs,
261+ .ensureFundsFor(web3, null, address, cost, signedTxs, () => {
265− () => { resolve(false); })
262+ resolve(false);
263+ })
264+ .then(res => {
266− .then(res => { resolve(res); });
265+ resolve(res);
266+ });
267 });
267 });
268 }
268 }
269
269
270 getChainTitle(chainId) {
270 getChainTitle(chainId) {
271 let map = {
271 let map = {
272− "1": "Mainnet - 1",
272+ '1': 'Mainnet - 1',
273− "2": "Morden - 2",
273+ '2': 'Morden - 2',
274− "3": "Ropsten - 3",
274+ '3': 'Ropsten - 3',
275− "4": "Goerli - 4",
275+ '4': 'Goerli - 4',
276− "42": "Kovan - 42",
276+ '42': 'Kovan - 42',
277− "1337": "Geth private chains - 1337",
277+ '1337': 'Geth private chains - 1337',
278− "custom": "Custom"
278+ custom: 'Custom',
279− }
279+ };
280−
280+
281− return map[chainId]
281+ return map[chainId];
282 }
282 }
283
283
284 getChainOptions() {
284 getChainOptions() {
285− return [{
285+ return [
286+ {
286− title: this.getChainTitle("1"),
287+ title: this.getChainTitle('1'),
287− value: "1"
288+ value: '1',
288− }, {
289+ },
290+ {
289− title: this.getChainTitle("2"),
291+ title: this.getChainTitle('2'),
290− value: "2"
292+ value: '2',
291− }, {
293+ },
294+ {
292− title: this.getChainTitle("3"),
295+ title: this.getChainTitle('3'),
293− value: "3"
296+ value: '3',
294− }, {
297+ },
298+ {
295− title: this.getChainTitle("4"),
299+ title: this.getChainTitle('4'),
296− value: "4"
300+ value: '4',
297− }, {
301+ },
302+ {
298− title: this.getChainTitle("42"),
303+ title: this.getChainTitle('42'),
299− value: "42"
304+ value: '42',
300− }, {
305+ },
306+ {
301− title: this.getChainTitle("1337"),
307+ title: this.getChainTitle('1337'),
302− value: "1337"
308+ value: '1337',
303− }, {
309+ },
310+ {
304− title: this.getChainTitle("custom"),
311+ title: this.getChainTitle('custom'),
305− value: "custom"
312+ value: 'custom',
313+ },
306− }]
314+ ];
307 }
315 }
308
316
309 render() {
317 render() {
310− const { web3, canGenerate } = this.props
318+ const { web3, canGenerate } = this.props;
311− const { gasPrice, gasLimit, nonce, chainId,
319+ const {
320+ gasPrice,
321+ gasLimit,
322+ nonce,
323+ chainId,
324+ txn,
325+ stx,
326+ userApproval,
312− txn, stx, userApproval, showGasDetails,
327+ showGasDetails,
313− customChain, txStatus } = this.state
328+ customChain,
329+ txStatus,
330+ } = this.state;
314
331
332+ const {
333+ setNonce,
334+ setChainId,
335+ setGasLimit,
336+ setGasPrice,
315− const { setNonce, setChainId, setGasLimit, setGasPrice, toggleGasDetails,
337+ toggleGasDetails,
338+ setUserApproval,
339+ sendTxn,
316− setUserApproval, sendTxn, createUnsignedTxn, handleChainUpdate } = this
340+ createUnsignedTxn,
341+ handleChainUpdate,
342+ } = this;
317
343
318− const { state } = this
344+ const { state } = this;
319
345
320− const canSign = Just.hasInstance(txn)
346+ const canSign = Just.hasInstance(txn);
321− const canApprove = Just.hasInstance(stx)
347+ const canApprove = Just.hasInstance(stx);
322− const canSend = Just.hasInstance(stx) && userApproval === true
348+ const canSend = Just.hasInstance(stx) && userApproval === true;
323
349
324− const chainOptions = this.getChainOptions()
350+ const chainOptions = this.getChainOptions();
325
351
326− const generateButtonColor =
352+ const generateButtonColor = Nothing.hasInstance(txn) ? 'blue' : 'green';
327− Nothing.hasInstance(txn)
328− ? 'blue'
329− : 'green'
330
353
331− const signerButtonColor =
354+ const signerButtonColor = Nothing.hasInstance(stx) ? 'blue' : 'green';
332− Nothing.hasInstance(stx)
333− ? 'blue'
334− : 'green'
335
355
336− const generateTxnButton =
356+ const generateTxnButton = (
337 <Button
357 <Button
338− className={ 'mt-8' }
358+ className={'mt-8'}
339− disabled={ !canGenerate }
359+ disabled={!canGenerate}
340− prop-color={ generateButtonColor }
360+ prop-color={generateButtonColor}
341− prop-size={ 'lg wide' }
361+ prop-size={'lg wide'}
342− onClick={ createUnsignedTxn }
362+ onClick={createUnsignedTxn}>
343− >
344− { 'Generate Transaction' }
363+ {'Generate Transaction'}
345 </Button>
364 </Button>
365+ );
346
366
347 const unsignedTxnDisplay = txn.matchWith({
367 const unsignedTxnDisplay = txn.matchWith({
348 Nothing: _ => '',
368 Nothing: _ => '',
349− Just: tx =>
369+ Just: tx => (
350 <React.Fragment>
370 <React.Fragment>
351− <H3 className={ 'mt-8' }>
352− { 'Unsigned Transaction' }
371+ <H3 className={'mt-8'}>{'Unsigned Transaction'}</H3>
353− </H3>
354− <Code>
355− { JSON.stringify(tx.value, null, 2) }
372+ <Code>{JSON.stringify(tx.value, null, 2)}</Code>
356− </Code>
357 </React.Fragment>
373 </React.Fragment>
374+ ),
358− })
375+ });
359
376
360 const gasPriceRangeDialogue = (
377 const gasPriceRangeDialogue = (
361 <React.Fragment>
378 <React.Fragment>
@@ -366,7 +383,9 @@ class StatelessTransaction extends React.Component {
366 </div>
383 </div>
367 <div className="text-sm">
384 <div className="text-sm">
368 <span>Max transaction fee: </span>
385 <span>Max transaction fee: </span>
386+ <span className="text-700">
369− <span className="text-700">{(gasPrice * gasLimit) / 1000000000} eth</span>
387+ {(gasPrice * gasLimit) / 1000000000} eth
388+ </span>
370 </div>
389 </div>
371 </div>
390 </div>
372
391
@@ -378,185 +397,182 @@ class StatelessTransaction extends React.Component {
378 list="gweiVals"
397 list="gweiVals"
379 value={gasPrice}
398 value={gasPrice}
380 onChange={this.rangeChange}
399 onChange={this.rangeChange}
381− />
400+ />
382
401
383 <div className="flex space-between text-sm mb-8">
402 <div className="flex space-between text-sm mb-8">
384 <div>Cheap</div>
403 <div>Cheap</div>
385 <div>Fast</div>
404 <div>Fast</div>
386 </div>
405 </div>
387 </React.Fragment>
406 </React.Fragment>
388− )
407+ );
389
408
390 const toggleGasDetailsDialogue = (
409 const toggleGasDetailsDialogue = (
391− <span className="text-link" onClick={toggleGasDetails}>Gas Details</span>
410+ <span className="text-link" onClick={toggleGasDetails}>
411+ Gas Details
412+ </span>
392− )
413+ );
393
414
394− const gasPriceDialogue =
415+ const gasPriceDialogue = (
395 <Input
416 <Input
396− className={ 'mono mt-4' }
417+ className={'mono mt-4'}
397− prop-size={ 'md' }
418+ prop-size={'md'}
398− prop-format={ 'innerLabel' }
419+ prop-format={'innerLabel'}
399− value={ gasPrice }
420+ value={gasPrice}
400− onChange={ setGasPrice }
421+ onChange={setGasPrice}>
401− >
402− <InnerLabel>
403− { 'Gas Price (gwei)' }
422+ <InnerLabel>{'Gas Price (gwei)'}</InnerLabel>
404− </InnerLabel>
405 </Input>
423 </Input>
424+ );
406
425
407− const gasLimitDialogue =
426+ const gasLimitDialogue = (
408 <Input
427 <Input
409− className={ 'mono mt-4' }
428+ className={'mono mt-4'}
410− prop-size={ 'md' }
429+ prop-size={'md'}
411− prop-format={ 'innerLabel' }
430+ prop-format={'innerLabel'}
412− value={ gasLimit }
431+ value={gasLimit}
413− onChange={ setGasLimit }>
432+ onChange={setGasLimit}>
414− <InnerLabel>
433+ <InnerLabel>{'Gas Limit'}</InnerLabel>
415− { 'Gas Limit' }
416− </InnerLabel>
417 </Input>
434 </Input>
435+ );
418
436
419− const nonceDialogue =
437+ const nonceDialogue = (
420 <Input
438 <Input
421− className={ 'mono mt-4 mb-4' }
439+ className={'mono mt-4 mb-4'}
422− prop-size={ 'md' }
440+ prop-size={'md'}
423− prop-format={ 'innerLabel' }
441+ prop-format={'innerLabel'}
424− value={ nonce }
442+ value={nonce}
425− onChange={ setNonce }
443+ onChange={setNonce}>
426− >
427− <InnerLabel>
444+ <InnerLabel>{'Nonce'}</InnerLabel>
428− { 'Nonce' }
429− </InnerLabel>
430 </Input>
445 </Input>
446+ );
431
447
432− const chainDialogueTitle = customChain ? "Custom" : this.getChainTitle(chainId)
448+ const chainDialogueTitle = customChain
449+ ? 'Custom'
450+ : this.getChainTitle(chainId);
433− const chainDialogue =
451+ const chainDialogue = (
434 <InnerLabelDropdown
452 <InnerLabelDropdown
435 className={'mt-6'}
453 className={'mt-6'}
436 fullWidth={true}
454 fullWidth={true}
437 title={'Chain ID'}
455 title={'Chain ID'}
438 options={chainOptions}
456 options={chainOptions}
439 handleUpdate={handleChainUpdate}
457 handleUpdate={handleChainUpdate}
440− currentSelectionTitle={chainDialogueTitle}
458+ currentSelectionTitle={chainDialogueTitle}></InnerLabelDropdown>
441− >
459+ );
442− </InnerLabelDropdown>
460+
443−
444− const customChainDialogue = !customChain ? null :
461+ const customChainDialogue = !customChain ? null : (
445− <Input
462+ <Input
446− className={ 'mono mt-4 mb-8' }
463+ className={'mono mt-4 mb-8'}
447− prop-size={ 'md' }
464+ prop-size={'md'}
448− prop-format={ 'innerLabel' }
465+ prop-format={'innerLabel'}
449− value={ chainId }
466+ value={chainId}
450− onChange={ setChainId }
467+ onChange={setChainId}>
451− >
452− <InnerLabel>
468+ <InnerLabel>{'Chain ID'}</InnerLabel>
453− { 'Chain ID' }
454− </InnerLabel>
455− </Input>
469+ </Input>
470+ );
456
471
457 const onlineParamsDialogue = web3.matchWith({
472 const onlineParamsDialogue = web3.matchWith({
458 Just: _ => <div />,
473 Just: _ => <div />,
459− Nothing: _ =>
474+ Nothing: _ => (
460 <React.Fragment>
475 <React.Fragment>
461− { nonceDialogue }
476+ {nonceDialogue}
462− { chainDialogue }
477+ {chainDialogue}
463− { customChainDialogue }
478+ {customChainDialogue}
464 </React.Fragment>
479 </React.Fragment>
480+ ),
465− })
481+ });
466
482
467− const signTxnButton =
483+ const signTxnButton = (
468 <Button
484 <Button
469− disabled={ !canSign }
485+ disabled={!canSign}
470− className={ 'mt-8' }
486+ className={'mt-8'}
471− prop-size={ 'lg wide' }
487+ prop-size={'lg wide'}
472− prop-color={ signerButtonColor }
488+ prop-color={signerButtonColor}
489+ onClick={() =>
473− onClick={ () => signTransaction({...this.props, ...this.state, setStx: this.setStx}) }
490+ signTransaction({ ...this.props, ...this.state, setStx: this.setStx })
474− >
491+ }>
475− { 'Sign Transaction' }
492+ {'Sign Transaction'}
476 </Button>
493 </Button>
494+ );
477
495
478 const signedTxnDisplay = stx.matchWith({
496 const signedTxnDisplay = stx.matchWith({
479 Nothing: _ => '',
497 Nothing: _ => '',
480− Just: tx =>
498+ Just: tx => (
481 <React.Fragment>
499 <React.Fragment>
482− <H3 className={ 'mt-8' }>
483− { 'Signed Transaction' }
500+ <H3 className={'mt-8'}>{'Signed Transaction'}</H3>
484− </H3>
485− <Code>
486− { JSON.stringify(renderSignedTx(tx.value), null, 2) }
501+ <Code>{JSON.stringify(renderSignedTx(tx.value), null, 2)}</Code>
487− </Code>
488 </React.Fragment>
502 </React.Fragment>
503+ ),
489− })
504+ });
490
505
491− const confirmButton =
506+ const confirmButton = (
492 <CheckboxButton
507 <CheckboxButton
493− className={ 'mt-8' }
508+ className={'mt-8'}
494− disabled={ !canApprove }
509+ disabled={!canApprove}
495− onToggle={ setUserApproval }
510+ onToggle={setUserApproval}
496− state={ userApproval }
511+ state={userApproval}
497− label='I approve this transaction and wish to send.'
512+ label="I approve this transaction and wish to send."></CheckboxButton>
498− ></CheckboxButton>
513+ );
499−
514+
500− const sending = (txStatus !== SUBMISSION_STATES.PROMPT);
515+ const sending = txStatus !== SUBMISSION_STATES.PROMPT;
501 const sendTxnSpinner = sending ? '' : 'hide';
516 const sendTxnSpinner = sending ? '' : 'hide';
502− const sendTxnButton =
517+ const sendTxnButton = (
503 <Button
518 <Button
504− prop-size={ 'xl wide' }
519+ prop-size={'xl wide'}
505− className={ 'mt-8' }
520+ className={'mt-8'}
506− disabled={ !canSend || sending }
521+ disabled={!canSend || sending}
507− onClick={ sendTxn }
522+ onClick={sendTxn}>
508− >
509 <span className="relative">
523 <span className="relative">
510 <span className={`btn-spinner ${sendTxnSpinner}`}></span>
524 <span className={`btn-spinner ${sendTxnSpinner}`}></span>
511− { txStatus }
525+ {txStatus}
512 </span>
526 </span>
513 </Button>
527 </Button>
528+ );
514
529
515 const sendDialogue = web3.matchWith({
530 const sendDialogue = web3.matchWith({
516 Nothing: _ => '',
531 Nothing: _ => '',
517− Just: _ =>
532+ Just: _ => (
518 <React.Fragment>
533 <React.Fragment>
519− { confirmButton }
534+ {confirmButton}
520− { sendTxnButton }
535+ {sendTxnButton}
521 </React.Fragment>
536 </React.Fragment>
537+ ),
522− })
538+ });
523−
539+
524− const txnErrorDialogue = Nothing.hasInstance(state.txError)
540+ const txnErrorDialogue = Nothing.hasInstance(state.txError) ? (
525− ? ''
541+ ''
542+ ) : (
526− : <Warning className={'mt-8'}>
543+ <Warning className={'mt-8'}>
527− <H3 style={{marginTop: 0, paddingTop: 0}}>
544+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
528− {
529− 'There was an error sending your transaction.'
545+ {'There was an error sending your transaction.'}
530− }
531− </H3>
546+ </H3>
532− { state.txError.value }
547+ {state.txError.value}
533 </Warning>
548 </Warning>
549+ );
534
550
535 return (
551 return (
536 <React.Fragment>
552 <React.Fragment>
537− { generateTxnButton }
553+ {generateTxnButton}
538− { unsignedTxnDisplay }
554+ {unsignedTxnDisplay}
539
555
540− { gasPriceRangeDialogue }
556+ {gasPriceRangeDialogue}
541− { toggleGasDetailsDialogue }
557+ {toggleGasDetailsDialogue}
542
558
543− { showGasDetails &&
559+ {showGasDetails && (
544 <div>
560 <div>
545− { gasPriceDialogue }
561+ {gasPriceDialogue}
546− { gasLimitDialogue }
562+ {gasLimitDialogue}
547 </div>
563 </div>
548− }
564+ )}
549− { onlineParamsDialogue }
565+ {onlineParamsDialogue}
550
566
551− { signTxnButton }
567+ {signTxnButton}
552
568
553− { signedTxnDisplay }
569+ {signedTxnDisplay}
554− { sendDialogue }
570+ {sendDialogue}
555
571
556− { txnErrorDialogue }
572+ {txnErrorDialogue}
557 </React.Fragment>
573 </React.Fragment>
558− )
574+ );
559 }
575 }
560}
576}
561
577
562−export default StatelessTransaction
578+export default StatelessTransaction;
src/bridge/components/Textarea.js+30−37
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
3import ProtoTextarea from './ProtoTextarea';
3import ProtoTextarea from './ProtoTextarea';
4
4
5const Textarea = props => {
5const Textarea = props => {
6−
7 const _disabled = props.disabled === true ? 'br-gray-30' : '';
6 const _disabled = props.disabled === true ? 'br-gray-30' : '';
8
7
9 // This enables all children of this component to share the state of this
8 // This enables all children of this component to share the state of this
@@ -15,16 +14,13 @@ const Textarea = props => {
15 focussed: props.focussed,
14 focussed: props.focussed,
16 beenFocussed: props.beenFocussed,
15 beenFocussed: props.beenFocussed,
17 error: props.error,
16 error: props.error,
18− }
17+ };
19− return React.cloneElement(child, propsToPass)
18+ return React.cloneElement(child, propsToPass);
20 });
19 });
21
20
22 const className = props.advanced
21 const className = props.advanced
23 ? `${props.valClassname} ${props.className}`
22 ? `${props.valClassname} ${props.className}`
24− : `br-gray-50 f-br-blue outline-blue ${props.className}`
23+ : `br-gray-50 f-br-blue outline-blue ${props.className}`;
25−
26−
27−
28
24
29 return (
25 return (
30 <div className={'relative'}>
26 <div className={'relative'}>
@@ -32,43 +28,40 @@ const Textarea = props => {
32 {...props}
28 {...props}
33 // prop-focussed={props.focussed.toString()}
29 // prop-focussed={props.focussed.toString()}
34 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
30 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
35− onChange={e => props.onChange(e.target.value)} />
31+ onChange={e => props.onChange(e.target.value)}
36− {
32+ />
37− props.displayFailure === true && props.advanced === true
33+ {props.displayFailure === true && props.advanced === true ? (
38− ? <div className={'yellow text-600 h-6 mt-1 fs-3 abs'}>{props.error}</div>
34+ <div className={'yellow text-600 h-6 mt-1 fs-3 abs'}>{props.error}</div>
35+ ) : (
39− : <div/>
36+ <div />
40− }
37+ )}
41− {
42− childrenWithProps
38+ {childrenWithProps}
43− }
44 </div>
39 </div>
45 );
40 );
46−}
41+};
47
42
48Textarea.propTypes = {
43Textarea.propTypes = {
49− focussed: PropTypes.bool,
44+ focussed: PropTypes.bool,
50− beenFocussed: PropTypes.bool,
45+ beenFocussed: PropTypes.bool,
51− hasSubmitted: PropTypes.bool,
46+ hasSubmitted: PropTypes.bool,
52− displayPass: PropTypes.bool,
47+ displayPass: PropTypes.bool,
53− displayFailure: PropTypes.bool,
48+ displayFailure: PropTypes.bool,
54− advanced: PropTypes.bool,
49+ advanced: PropTypes.bool,
55− error: PropTypes.string,
50+ error: PropTypes.string,
56− valClassname: PropTypes.string,
51+ valClassname: PropTypes.string,
57− size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
52+ size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
58};
53};
59
54
60−
61Textarea.defaultProps = {
55Textarea.defaultProps = {
62− focussed: false,
56+ focussed: false,
63− beenFocussed: false,
57+ beenFocussed: false,
64− hasSubmitted: false,
58+ hasSubmitted: false,
65− displayPass: false,
59+ displayPass: false,
66− displayFailure: false,
60+ displayFailure: false,
67− advanced: false,
61+ advanced: false,
68− error: '',
62+ error: '',
69− size: 'm',
63+ size: 'm',
70− format: 'normal',
64+ format: 'normal',
71};
65};
72
66
73−
74export default Textarea;
67export default Textarea;
src/bridge/components/UploadButton.js+12−10
@@ -12,16 +12,18 @@ class UploadButton extends React.Component {
12
12
13 render() {
13 render() {
14 return (
14 return (
15− <label className={this.props.className}>
15+ <label className={this.props.className}>
16− <input
16+ <input
17− className={ 'hidden h-0 w-0 abs' }
17+ className={'hidden h-0 w-0 abs'}
18+ ref={ref => {
18− ref={(ref) => { this.input = ref; }}
19+ this.input = ref;
20+ }}
19− onChange={this.onChange}
21+ onChange={this.onChange}
20− disabled={this.props.disabled}
22+ disabled={this.props.disabled}
21− type="file"
23+ type="file"
22− />
24+ />
23− {this.props.children}
25+ {this.props.children}
24− </label>
26+ </label>
25 );
27 );
26 }
28 }
27}
29}
src/bridge/components/ValidatedBlockie.js+24−26
@@ -1,6 +1,6 @@
1−import React from 'react'
1+import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3−import makeBlockie from 'ethereum-blockies-base64'
3+import makeBlockie from 'ethereum-blockies-base64';
4
4
5const ValidatedBlockie = ({
5const ValidatedBlockie = ({
6 className,
6 className,
@@ -13,42 +13,42 @@ const ValidatedBlockie = ({
13 displayPass,
13 displayPass,
14 displayFailure,
14 displayFailure,
15 focussed,
15 focussed,
16− beenFocussed
16+ beenFocussed,
17}) => {
17}) => {
18−
19 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
18 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
20
19
21 return (
20 return (
22 <div className={`${className}`} style={style}>
21 <div className={`${className}`} style={style}>
23− { displayPass
22+ {displayPass ? (
24− ? <img
23+ <img
25− alt={ address }
24+ alt={address}
26− src={ makeBlockie(address) }
25+ src={makeBlockie(address)}
27 style={{
26 style={{
28 width: size,
27 width: size,
29 height: size,
28 height: size,
30− }} />
29+ }}
30+ />
31+ ) : (
32+ <div
31− : <div className={''} style={{
33+ className={''}
34+ style={{
32− backgroundColor: colorStyle[1],
35+ backgroundColor: colorStyle[1],
33− width: size,
36+ width: size,
34− height: size,
37+ height: size,
35− }} />
38+ }}
39+ />
36− }
40+ )}
37− {
38− children
41+ {children}
39− }
40 </div>
42 </div>
41− )
43+ );
42−}
44+};
43−
44
45
45const styleSelector = (pass, fail, focus) => {
46const styleSelector = (pass, fail, focus) => {
46 if (fail === true) return ['#FFFFFF', '#F8C134'];
47 if (fail === true) return ['#FFFFFF', '#F8C134'];
47 if (pass === true) return ['#FFFFFF', '#2AA779'];
48 if (pass === true) return ['#FFFFFF', '#2AA779'];
48 if (focus === true) return ['#FFFFFF', '#4330FC'];
49 if (focus === true) return ['#FFFFFF', '#4330FC'];
49 return ['#FFFFFF', '#7F7F7F'];
50 return ['#FFFFFF', '#7F7F7F'];
50−}
51+};
51−
52
52
53ValidatedBlockie.propTypes = {
53ValidatedBlockie.propTypes = {
54 className: PropTypes.string,
54 className: PropTypes.string,
@@ -56,11 +56,9 @@ ValidatedBlockie.propTypes = {
56 style: PropTypes.object,
56 style: PropTypes.object,
57};
57};
58
58
59−
60ValidatedBlockie.defaultProps = {
59ValidatedBlockie.defaultProps = {
61 className: '',
60 className: '',
62 style: {},
61 style: {},
63};
62};
64
63
65−
66export default ValidatedBlockie;
64export default ValidatedBlockie;
src/bridge/components/ValidatedSigil.js+33−37
@@ -1,20 +1,19 @@
1−import React from 'react'
1+import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4−import { pour } from 'sigil-js'
4+import { pour } from 'sigil-js';
5−import * as ob from 'urbit-ob'
5+import * as ob from 'urbit-ob';
6−import ReactSVGComponents from '../components/ReactSVGComponents'
6+import ReactSVGComponents from '../components/ReactSVGComponents';
7
7
8const isValidPatp = patp => {
8const isValidPatp = patp => {
9− let isValid = false
9+ let isValid = false;
10 try {
10 try {
11− isValid = ob.isValidPatp(patp)
11+ isValid = ob.isValidPatp(patp);
12− } catch(err) {
12+ } catch (err) {
13− return isValid
13+ return isValid;
14 }
14 }
15− return isValid
15+ return isValid;
16−}
16+};
17−
18
17
19const ValidatedSigil = ({
18const ValidatedSigil = ({
20 className,
19 className,
@@ -27,41 +26,40 @@ const ValidatedSigil = ({
27 displayPass,
26 displayPass,
28 displayFailure,
27 displayFailure,
29 focussed,
28 focussed,
30− beenFocussed
29+ beenFocussed,
31}) => {
30}) => {
32−
33 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
31 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
34
32
35 return (
33 return (
36 <div className={`${className}`} style={style}>
34 <div className={`${className}`} style={style}>
37− {
38− isValidPatp(patp)
35+ {isValidPatp(patp) ? (
39− ? pour({
36+ pour({
40− patp: patp,
37+ patp: patp,
41− renderer: ReactSVGComponents,
38+ renderer: ReactSVGComponents,
42− size: size,
39+ size: size,
43− colorway: colorStyle,
40+ colorway: colorStyle,
44− margin: margin,
41+ margin: margin,
45− })
42+ })
43+ ) : (
44+ <div
46− : <div className={'bg-transparent'} style={{
45+ className={'bg-transparent'}
46+ style={{
47− width: size,
47+ width: size,
48− height: size,
48+ height: size,
49− }} />
49+ }}
50+ />
50− }
51+ )}
51− {
52− children
52+ {children}
53− }
54 </div>
53 </div>
55− )
54+ );
56−}
55+};
57
56
58const styleSelector = (pass, fail, focus) => {
57const styleSelector = (pass, fail, focus) => {
59 if (fail === true) return ['#FFFFFF', '#F8C134'];
58 if (fail === true) return ['#FFFFFF', '#F8C134'];
60 if (pass === true) return ['#FFFFFF', '#2AA779'];
59 if (pass === true) return ['#FFFFFF', '#2AA779'];
61 if (focus === true) return ['#FFFFFF', '#4330FC'];
60 if (focus === true) return ['#FFFFFF', '#4330FC'];
62 return ['#FFFFFF', '#7F7F7F'];
61 return ['#FFFFFF', '#7F7F7F'];
63−}
62+};
64−
65
63
66ValidatedSigil.propTypes = {
64ValidatedSigil.propTypes = {
67 className: PropTypes.string,
65 className: PropTypes.string,
@@ -69,11 +67,9 @@ ValidatedSigil.propTypes = {
69 style: PropTypes.object,
67 style: PropTypes.object,
70};
68};
71
69
72−
73ValidatedSigil.defaultProps = {
70ValidatedSigil.defaultProps = {
74 className: '',
71 className: '',
75 style: {},
72 style: {},
76};
73};
77
74
78−
79export default ValidatedSigil;
75export default ValidatedSigil;
src/bridge/components/Warning.js+4−9
@@ -1,16 +1,13 @@
1import React from 'react';
1import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
3
3
4const Warning = ({ className, children, style, measure }) => {
4const Warning = ({ className, children, style, measure }) => {
5−
6−
7 return (
5 return (
8 <div className={`mb-7 p-4 bg-yellow break-word ${className}`} style={style}>
6 <div className={`mb-7 p-4 bg-yellow break-word ${className}`} style={style}>
9− { children }
7+ {children}
10 </div>
8 </div>
11− )
9+ );
12−}
10+};
13−
14
11
15Warning.propTypes = {
12Warning.propTypes = {
16 className: PropTypes.string,
13 className: PropTypes.string,
@@ -18,11 +15,9 @@ Warning.propTypes = {
18 style: PropTypes.object,
15 style: PropTypes.object,
19};
16};
20
17
21−
22Warning.defaultProps = {
18Warning.defaultProps = {
23 className: '',
19 className: '',
24 style: {},
20 style: {},
25};
21};
26
22
27−
28export default Warning;
23export default Warning;
src/bridge/components/advancedInput.js+53−59
@@ -14,35 +14,30 @@ const initMessage = data => ({
14
14
15// Validation display triggers
15// Validation display triggers
16const displayTriggers = {
16const displayTriggers = {
17− eagerSuccess: r => t(r.pass),
17+ eagerSuccess: r => t(r.pass),
18− eagerFailure: r => f(r.pass),
18+ eagerFailure: r => f(r.pass),
19− tactfulSuccess: r => t(r.pass) && t(r.beenFocussed),
19+ tactfulSuccess: r => t(r.pass) && t(r.beenFocussed),
20− tactfulFailure: r => f(r.pass) && t(r.beenFocussed) && f(r.focussed),
20+ tactfulFailure: r => f(r.pass) && t(r.beenFocussed) && f(r.focussed),
21−}
21+};
22
22
23// =============================================================================
23// =============================================================================
24// AdvancedInput
24// AdvancedInput
25
25
26−const advancedInput = ({
26+const advancedInput = ({ WrappedComponent, validators, transformers }) => {
27− WrappedComponent,
28− validators,
29− transformers,
30−}) => {
31−
32 // set argument defaults
27 // set argument defaults
33− validators = defaultTo(validators, [])
28+ validators = defaultTo(validators, []);
34− transformers = defaultTo(transformers, [])
29+ transformers = defaultTo(transformers, []);
35
30
36 class AdvancedInputHOC extends Component {
31 class AdvancedInputHOC extends Component {
37 constructor(props) {
32 constructor(props) {
38− super(props)
33+ super(props);
39 this.state = {
34 this.state = {
40 focussed: false,
35 focussed: false,
41 beenFocussed: false,
36 beenFocussed: false,
42− }
37+ };
43− this.onFocus = this.onFocus.bind(this)
38+ this.onFocus = this.onFocus.bind(this);
44− this.onBlur = this.onBlur.bind(this)
39+ this.onBlur = this.onBlur.bind(this);
45− this.onChange = this.onChange.bind(this)
40+ this.onChange = this.onChange.bind(this);
46 }
41 }
47
42
48 onFocus = () => this.setState({ focussed: true });
43 onFocus = () => this.setState({ focussed: true });
@@ -50,8 +45,7 @@ const advancedInput = ({
50 onBlur = () => this.setState({ focussed: false, beenFocussed: true });
45 onBlur = () => this.setState({ focussed: false, beenFocussed: true });
51
46
52 onChange = value => {
47 onChange = value => {
53−
54− const { props } = this
48+ const { props } = this;
55
49
56 // =======================================================================
50 // =======================================================================
57 // 0. RUN VALUE TRANSFORMERS
51 // 0. RUN VALUE TRANSFORMERS
@@ -61,14 +55,13 @@ const advancedInput = ({
61 // should always have a sig prepending them, but users might not know/care
55 // should always have a sig prepending them, but users might not know/care
62 // about such a technicality. ValueTransformers are executed from right to
56 // about such a technicality. ValueTransformers are executed from right to
63 // left.
57 // left.
64− const newValue = compose(...transformers)(value)
58+ const newValue = compose(...transformers)(value);
65
59
66− props.onChange(newValue)
60+ props.onChange(newValue);
67− }
61+ };
68
62
69 render() {
63 render() {
70−
71− const { state, props } = this
64+ const { state, props } = this;
72
65
73 // =======================================================================
66 // =======================================================================
74 // RUN VALIDATORS AND DETERMINE STYLES
67 // RUN VALIDATORS AND DETERMINE STYLES
@@ -76,16 +69,19 @@ const advancedInput = ({
76 // Run validation chain on the transformed value. Validators are executed
69 // Run validation chain on the transformed value. Validators are executed
77 // from right to left and exit on the first validation failure.
70 // from right to left and exit on the first validation failure.
78
71
79− const report = compose(...validators, initMessage)(props.value)
72+ const report = compose(
73+ ...validators,
74+ initMessage
75+ )(props.value);
80
76
81 // Makes a uniform data structure to pass to display triggers. This
77 // Makes a uniform data structure to pass to display triggers. This
82 // removes the need to rely on argument order for these functions.
78 // removes the need to rely on argument order for these functions.
83 const stateReport = {
79 const stateReport = {
84− pass: report.pass,
80+ pass: report.pass,
85 beenFocussed: state.beenFocussed,
81 beenFocussed: state.beenFocussed,
86− focussed: state.focussed,
82+ focussed: state.focussed,
87− submitted: props.submitted,
83+ submitted: props.submitted,
88− }
84+ };
89
85
90 // Determine if the passing validation style or message should be shown
86 // Determine if the passing validation style or message should be shown
91 const displayPass = displayTriggers.eagerSuccess(stateReport);
87 const displayPass = displayTriggers.eagerSuccess(stateReport);
@@ -95,49 +91,46 @@ const advancedInput = ({
95
91
96 // Generate class strings for validation state. VCN stands for validation
92 // Generate class strings for validation state. VCN stands for validation
97 // class names
93 // class names
98− const valClassname = styleSelector(
94+ const valClassname = styleSelector(displayPass, displayFailure);
99− displayPass,
100− displayFailure
101− );
102
95
103 return (
96 return (
104 <WrappedComponent
97 <WrappedComponent
105 {...props}
98 {...props}
106 // Events
99 // Events
107− onFocus={ this.onFocus }
100+ onFocus={this.onFocus}
108− onBlur={ this.onBlur }
101+ onBlur={this.onBlur}
109− onChange={ this.onChange }
102+ onChange={this.onChange}
110−
111 // stateReport
103 // stateReport
112− focussed={ state.focussed }
104+ focussed={state.focussed}
113− beenFocussed={ state.beenFocussed }
105+ beenFocussed={state.beenFocussed}
114− displayPass={ displayPass }
106+ displayPass={displayPass}
115− displayFailure={ displayFailure }
107+ displayFailure={displayFailure}
116 // 'error' prop refers to an error message string, not the presence
108 // 'error' prop refers to an error message string, not the presence
117 // of an error.
109 // of an error.
118− error={ report.error }
110+ error={report.error}
119− didPass={ report.pass }
111+ didPass={report.pass}
120 value={props.value}
112 value={props.value}
121− className={ props.className }
113+ className={props.className}
122− disabled={ props.disabled }
114+ disabled={props.disabled}
123 valClassname={valClassname}
115 valClassname={valClassname}
124− advanced={true} />
116+ advanced={true}
117+ />
125− )
118+ );
126 }
119 }
127 }
120 }
128
121
129 AdvancedInputHOC.propTypes = {
122 AdvancedInputHOC.propTypes = {
130− className: PropTypes.string,
123+ className: PropTypes.string,
131− style: PropTypes.object,
124+ style: PropTypes.object,
132− value: PropTypes.string,
125+ value: PropTypes.string,
133− submitted: PropTypes.bool,
126+ submitted: PropTypes.bool,
134 };
127 };
135
128
136 AdvancedInputHOC.defaultProps = {
129 AdvancedInputHOC.defaultProps = {
137− className: '',
130+ className: '',
138− style: {},
131+ style: {},
139− value: '',
132+ value: '',
140− submitted: false,
133+ submitted: false,
141 };
134 };
142
135
143 return AdvancedInputHOC;
136 return AdvancedInputHOC;
@@ -145,9 +138,10 @@ const advancedInput = ({
145};
138};
146
139
147const styleSelector = (pass, fail) => {
140const styleSelector = (pass, fail) => {
141+ if (fail === true)
148− if (fail === true) return 'br-yellow outline-yellow f-br-yellow bg-yellow-light';
142+ return 'br-yellow outline-yellow f-br-yellow bg-yellow-light';
149 if (pass === true) return 'br-green outline-green f-br-green';
143 if (pass === true) return 'br-green outline-green f-br-green';
150− return 'br-gray-50 f-br-blue outline-blue'
144+ return 'br-gray-50 f-br-blue outline-blue';
151−}
145+};
152
146
153export default advancedInput;
147export default advancedInput;
src/bridge/components/icons/Chevron.js+6−7
@@ -1,16 +1,16 @@
1−import React from 'react'
1+import React from 'react';
2import PropTypes from 'prop-types';
2import PropTypes from 'prop-types';
3
3
4const Chevron = props => {
4const Chevron = props => {
5 return (
5 return (
6 <img
6 <img
7 src={`data:image/svg+xml;utf8,<svg width="11" height="24" viewBox="0 0 11 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 12L10.387 12.3166L10.646 12L10.387 11.6833L10 12ZM0.613022 1.31662L9.61302 12.3166L10.387 11.6833L1.38698 0.68338L0.613022 1.31662ZM9.61302 11.6833L0.613021 22.6834L1.38698 23.3166L10.387 12.3166L9.61302 11.6833Z" stroke="${props.color}" stroke-width="${props.strokeWidth}" fill="${props.color}"/></svg>`}
7 src={`data:image/svg+xml;utf8,<svg width="11" height="24" viewBox="0 0 11 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 12L10.387 12.3166L10.646 12L10.387 11.6833L10 12ZM0.613022 1.31662L9.61302 12.3166L10.387 11.6833L1.38698 0.68338L0.613022 1.31662ZM9.61302 11.6833L0.613021 22.6834L1.38698 23.3166L10.387 12.3166L9.61302 11.6833Z" stroke="${props.color}" stroke-width="${props.strokeWidth}" fill="${props.color}"/></svg>`}
8− alt=''
8+ alt=""
9 style={props.style}
9 style={props.style}
10 className={props.className}
10 className={props.className}
11 />
11 />
12− )
12+ );
13−}
13+};
14
14
15Chevron.propTypes = {
15Chevron.propTypes = {
16 style: PropTypes.object,
16 style: PropTypes.object,
@@ -19,12 +19,11 @@ Chevron.propTypes = {
19 strokeWidth: PropTypes.string,
19 strokeWidth: PropTypes.string,
20};
20};
21
21
22−
23Chevron.defaultProps = {
22Chevron.defaultProps = {
24 style: {},
23 style: {},
25 className: '',
24 className: '',
26 color: 'black',
25 color: 'black',
27− strokeWidth: '1px'
26+ strokeWidth: '1px',
28};
27};
29
28
30−export default Chevron
29+export default Chevron;
src/bridge/lib/base.js+5−8
@@ -1,9 +1,6 @@
1+const getDomProps = props =>
1−const getDomProps = props => Object.entries(props).reduce((acc, [k, v]) => {
2+ Object.entries(props).reduce((acc, [k, v]) => {
2− return k.match(/prop-/)
3+ return k.match(/prop-/) ? { ...acc, [k]: v } : acc;
3− ? { ...acc, [k]: v }
4− : acc
5−}, {})
4+ }, {});
6
5
7−export {
8− getDomProps
6+export { getDomProps };
9−}
src/bridge/lib/child.js+11−13
@@ -1,20 +1,18 @@
1−const MIN_GALAXY = 0
1+const MIN_GALAXY = 0;
2−const MIN_STAR = Math.pow(2, 8)
2+const MIN_STAR = Math.pow(2, 8);
3−const MIN_PLANET = Math.pow(2, 16)
3+const MIN_PLANET = Math.pow(2, 16);
4−const MAX_GALAXY = MIN_STAR - 1
4+const MAX_GALAXY = MIN_STAR - 1;
5−const MAX_STAR = MIN_PLANET - 1
5+const MAX_STAR = MIN_PLANET - 1;
6
6
7const randomStarName = galaxy =>
7const randomStarName = galaxy =>
8− (Math.floor(Math.random() * MAX_GALAXY) + 1) * MIN_STAR + galaxy
8+ (Math.floor(Math.random() * MAX_GALAXY) + 1) * MIN_STAR + galaxy;
9
9
10const randomPlanetName = star =>
10const randomPlanetName = star =>
11− (Math.floor(Math.random() * MAX_STAR) + 1) * (MAX_STAR + 1) + star
11+ (Math.floor(Math.random() * MAX_STAR) + 1) * (MAX_STAR + 1) + star;
12
12
13const getSpawnCandidate = point =>
13const getSpawnCandidate = point =>
14− point >= MIN_GALAXY && point < MIN_STAR
14+ point >= MIN_GALAXY && point < MIN_STAR
15− ? randomStarName(point)
15+ ? randomStarName(point)
16− : randomPlanetName(point)
16+ : randomPlanetName(point);
17
17
18−export {
19− getSpawnCandidate
18+export { getSpawnCandidate };
20−}
src/bridge/lib/contracts.js+5−9
@@ -1,26 +1,22 @@
1−
2const CONTRACT_ADDRESSES = {
1const CONTRACT_ADDRESSES = {
3 DEV: {
2 DEV: {
4 ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480',
3 ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480',
5 azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381',
4 azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381',
6 polls: '0x935452c45eda2958976a429c9733c40302995efd',
5 polls: '0x935452c45eda2958976a429c9733c40302995efd',
7− delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43'
6+ delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43',
8 },
7 },
9 ROPSTEN: {
8 ROPSTEN: {
10 ecliptic: '0x8b9f86a28921d9c705b3113a755fb979e1bd1bce',
9 ecliptic: '0x8b9f86a28921d9c705b3113a755fb979e1bd1bce',
11 azimuth: '0x308ab6a6024cf198b57e008d0ac9ad0219886579',
10 azimuth: '0x308ab6a6024cf198b57e008d0ac9ad0219886579',
12− polls: '0xf5DA85f0d285A0F88af2388DD177A221872C8971'
11+ polls: '0xf5DA85f0d285A0F88af2388DD177A221872C8971',
13 //TODO delegatedSending
12 //TODO delegatedSending
14 },
13 },
15 MAINNET: {
14 MAINNET: {
16 ecliptic: '0x6ac07b7c4601b5ce11de8dfe6335b871c7c4dd4d',
15 ecliptic: '0x6ac07b7c4601b5ce11de8dfe6335b871c7c4dd4d',
17 azimuth: '0x223c067f8cf28ae173ee5cafea60ca44c335fecb',
16 azimuth: '0x223c067f8cf28ae173ee5cafea60ca44c335fecb',
18− polls: '0x7fecab617c868bb5996d99d95200d2fa708218e4'
17+ polls: '0x7fecab617c868bb5996d99d95200d2fa708218e4',
19 //TODO delegatedSending
18 //TODO delegatedSending
20 },
19 },
21−}
20+};
22−
23−export {
24− CONTRACT_ADDRESSES
25−}
26
21
22+export { CONTRACT_ADDRESSES };
src/bridge/lib/error.js+19−27
@@ -1,4 +1,3 @@
1−
2const BRIDGE_ERROR = {
1const BRIDGE_ERROR = {
3 MISSING_WEB3: new Error('no web3 object found'),
2 MISSING_WEB3: new Error('no web3 object found'),
4 MISSING_WALLET: new Error('no wallet found'),
3 MISSING_WALLET: new Error('no wallet found'),
@@ -11,36 +10,29 @@ const BRIDGE_ERROR = {
11 INVALID_ROUTE: new Error('invalid route'),
10 INVALID_ROUTE: new Error('invalid route'),
12 INVALID_NETWORK_TYPE: new Error('invalid network type'),
11 INVALID_NETWORK_TYPE: new Error('invalid network type'),
13 INVALID_WALLET_TYPE: new Error('invalid wallet type'),
12 INVALID_WALLET_TYPE: new Error('invalid wallet type'),
14−}
13+};
15
14
16const renderTxnError = (web3, msg) => {
15const renderTxnError = (web3, msg) => {
17 const txnCost = (web3, msg) => {
16 const txnCost = (web3, msg) => {
18− const costWei = msg.match(/upfront cost is: ([0-9]+)/)
17+ const costWei = msg.match(/upfront cost is: ([0-9]+)/);
19− const costEth = web3.utils.fromWei(costWei[1], 'ether')
18+ const costEth = web3.utils.fromWei(costWei[1], 'ether');
20− return parseFloat(costEth).toFixed(4)
19+ return parseFloat(costEth).toFixed(4);
21− }
20+ };
22
21
23 const acctBalance = (web3, msg) => {
22 const acctBalance = (web3, msg) => {
24− const balWei = msg.match(/sender's account only has: ([0-9]+)/)
23+ const balWei = msg.match(/sender's account only has: ([0-9]+)/);
25− const balEth = web3.utils.fromWei(balWei[1], 'ether')
24+ const balEth = web3.utils.fromWei(balWei[1], 'ether');
26− return parseFloat(balEth).toFixed(4)
25+ return parseFloat(balEth).toFixed(4);
27− }
26+ };
28−
29− return (
30− msg.includes("doesn't have enough funds")
31−
32− ? "The account doesn't have enough Ether. " +
33− `The up-front transaction cost is ${txnCost(web3, msg)} ETH, but the ` +
34− `account only holds ${acctBalance(web3, msg)} ETH.`
35
27
28+ return msg.includes("doesn't have enough funds")
29+ ? "The account doesn't have enough Ether. " +
30+ `The up-front transaction cost is ${txnCost(web3, msg)} ETH, but the ` +
31+ `account only holds ${acctBalance(web3, msg)} ETH.`
36− : "You may want to verify that the account that has signed the " +
32+ : 'You may want to verify that the account that has signed the ' +
37− "transaction holds sufficient Ether to pay for the gas, and " +
33+ 'transaction holds sufficient Ether to pay for the gas, and ' +
38− "also that you haven't set the gas limit for the transaction " +
34+ "also that you haven't set the gas limit for the transaction " +
39− "too low."
35+ 'too low.';
40− )
41−}
36+};
42
37
43−export {
44− BRIDGE_ERROR,
45− renderTxnError
38+export { BRIDGE_ERROR, renderTxnError };
46−}
src/bridge/lib/invite.js+105−78
@@ -1,69 +1,69 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import Tx from 'ethereumjs-tx'
2+import Tx from 'ethereumjs-tx';
3
3
4−import * as azimuth from 'azimuth-js'
4+import * as azimuth from 'azimuth-js';
5−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
5+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
6−import * as wg from '../../walletgen/lib/lib.js'
6+import * as wg from '../../walletgen/lib/lib.js';
7−import * as tank from './tank'
7+import * as tank from './tank';
8
8
9−import JSZip from 'jszip'
9+import JSZip from 'jszip';
10−import saveAs from 'file-saver'
10+import saveAs from 'file-saver';
11
11
12−import { sendSignedTransaction } from './txn'
12+import { sendSignedTransaction } from './txn';
13−import { BRIDGE_ERROR } from '../lib/error'
13+import { BRIDGE_ERROR } from '../lib/error';
14−import { attemptSeedDerivation } from './keys'
14+import { attemptSeedDerivation } from './keys';
15−import { addHexPrefix, WALLET_NAMES } from './wallet'
15+import { addHexPrefix, WALLET_NAMES } from './wallet';
16
16
17const INVITE_STAGES = {
17const INVITE_STAGES = {
18− INVITE_LOGIN: "invite login",
18+ INVITE_LOGIN: 'invite login',
19− INVITE_WALLET: "invite wallet",
19+ INVITE_WALLET: 'invite wallet',
20− INVITE_VERIFY: "invite verify",
20+ INVITE_VERIFY: 'invite verify',
21− INVITE_TRANSACTIONS: "invite transactions"
21+ INVITE_TRANSACTIONS: 'invite transactions',
22−}
22+};
23
23
24const WALLET_STATES = {
24const WALLET_STATES = {
25− UNLOCKING: "Unlocking invite wallet",
25+ UNLOCKING: 'Unlocking invite wallet',
26− GENERATING: "Generating your wallet",
26+ GENERATING: 'Generating your wallet',
27− RENDERING: "Creating paper collateral",
27+ RENDERING: 'Creating paper collateral',
28− PAPER_READY: "Download your wallet",
28+ PAPER_READY: 'Download your wallet',
29− DOWNLOADED: "Wallet downloaded",
29+ DOWNLOADED: 'Wallet downloaded',
30− TRANSACTIONS: "Sending transactions"
30+ TRANSACTIONS: 'Sending transactions',
31−}
31+};
32
32
33const TRANSACTION_STATES = {
33const TRANSACTION_STATES = {
34 GENERATING: {
34 GENERATING: {
35 label: 'Generating transactions',
35 label: 'Generating transactions',
36− pct: "0%"
36+ pct: '0%',
37 },
37 },
38 SIGNING: {
38 SIGNING: {
39 label: 'Signing transactions',
39 label: 'Signing transactions',
40− pct: "15%"
40+ pct: '15%',
41 },
41 },
42 FUNDING_INVITE: {
42 FUNDING_INVITE: {
43 label: 'Funding invite wallet',
43 label: 'Funding invite wallet',
44− pct: "30%"
44+ pct: '30%',
45 },
45 },
46 CLAIMING: {
46 CLAIMING: {
47 label: 'Claiming invite',
47 label: 'Claiming invite',
48− pct: "55%"
48+ pct: '55%',
49 },
49 },
50 FUNDING_RECIPIENT: {
50 FUNDING_RECIPIENT: {
51 label: 'Funding recipient wallet',
51 label: 'Funding recipient wallet',
52− pct: "65%"
52+ pct: '65%',
53 },
53 },
54 CONFIGURING: {
54 CONFIGURING: {
55 label: 'Configuring planet',
55 label: 'Configuring planet',
56− pct: "85%"
56+ pct: '85%',
57 },
57 },
58 CLEANING: {
58 CLEANING: {
59 label: 'Cleaning up',
59 label: 'Cleaning up',
60− pct: "95%"
60+ pct: '95%',
61 },
61 },
62 DONE: {
62 DONE: {
63 label: 'Done',
63 label: 'Done',
64− pct: "100%"
64+ pct: '100%',
65 },
65 },
66−}
66+};
67
67
68async function generateWallet(point) {
68async function generateWallet(point) {
69 const ticket = await wg.makeTicket(point);
69 const ticket = await wg.makeTicket(point);
@@ -83,35 +83,42 @@ async function downloadWallet(paper) {
83 const bin3 = paper.filter(item => item.bin === '3');
83 const bin3 = paper.filter(item => item.bin === '3');
84 const bin4 = paper.filter(item => item.bin === '4');
84 const bin4 = paper.filter(item => item.bin === '4');
85
85
86− const bin0Folder = zip.folder("0. Public");
86+ const bin0Folder = zip.folder('0. Public');
87− const bin1Folder = zip.folder("1. Very High Friction Custody");
87+ const bin1Folder = zip.folder('1. Very High Friction Custody');
88− const bin2Folder = zip.folder("2. High Friction Custody");
88+ const bin2Folder = zip.folder('2. High Friction Custody');
89− const bin3Folder = zip.folder("3. Medium Friction Custody");
89+ const bin3Folder = zip.folder('3. Medium Friction Custody');
90− const bin4Folder = zip.folder("4. Low Friction Custody");
90+ const bin4Folder = zip.folder('4. Low Friction Custody');
91
91
92− bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png))
92+ bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
93 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
93 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
94 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
94 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
95 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
95 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
96 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
96 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
97
97
98− zip.generateAsync({type:"blob"}).then((content) => {
98+ zip.generateAsync({ type: 'blob' }).then(content => {
99 saveAs(content, 'urbit-wallet.zip');
99 saveAs(content, 'urbit-wallet.zip');
100− resolve(true)
100+ resolve(true);
101 });
101 });
102− })
102+ });
103}
103}
104
104
105async function startTransactions(args) {
105async function startTransactions(args) {
106+ let {
106− let { realPointM, web3M, contractsM,
107+ realPointM,
108+ web3M,
109+ contractsM,
110+ inviteWalletM,
111+ realWalletM,
107− inviteWalletM, realWalletM, setUrbitWallet, updateProgress } = args
112+ setUrbitWallet,
113+ updateProgress,
114+ } = args;
108
115
109 const askForFunding = (address, amount, current) => {
116 const askForFunding = (address, amount, current) => {
110 updateProgress({
117 updateProgress({
111− type: "notify",
118+ type: 'notify',
112− value: `Please make sure ${address} has at least ${amount} wei, we'll continue once that's true. Current balance: ${current}. Waiting`
119+ value: `Please make sure ${address} has at least ${amount} wei, we'll continue once that's true. Current balance: ${current}. Waiting`,
113 });
120 });
114− }
121+ };
115
122
116 if (Nothing.hasInstance(web3M)) {
123 if (Nothing.hasInstance(web3M)) {
117 throw BRIDGE_ERROR.MISSING_WEB3;
124 throw BRIDGE_ERROR.MISSING_WEB3;
@@ -145,7 +152,11 @@ async function startTransactions(args) {
145
152
146 // transfer from invite wallet to new wallet
153 // transfer from invite wallet to new wallet
147
154
148− let transferTx = azimuth.ecliptic.transferPoint(contracts, point, realWallet.ownership.keys.address);
155+ let transferTx = azimuth.ecliptic.transferPoint(
156+ contracts,
157+ point,
158+ realWallet.ownership.keys.address
159+ );
149 transferTx.gas = 500000; //TODO can maybe be lower?
160 transferTx.gas = 500000; //TODO can maybe be lower?
150
161
151 // ping gas tank with txs if needed
162 // ping gas tank with txs if needed
@@ -158,20 +169,25 @@ async function startTransactions(args) {
158 //NOTE using web3.eth.accounts.signTransaction is broken (1.0.0-beta51)
169 //NOTE using web3.eth.accounts.signTransaction is broken (1.0.0-beta51)
159 let transferStx = new Tx(transferTx);
170 let transferStx = new Tx(transferTx);
160 transferStx.sign(inviteWallet.privateKey);
171 transferStx.sign(inviteWallet.privateKey);
161− let rawTransferStx = '0x'+transferStx.serialize().toString('hex');
172+ let rawTransferStx = '0x' + transferStx.serialize().toString('hex');
162
173
163 const transferCost = transferTx.gas * transferTx.gasPrice;
174 const transferCost = transferTx.gas * transferTx.gasPrice;
164 updateProgress({
175 updateProgress({
165− type: "progress",
176+ type: 'progress',
166− value: TRANSACTION_STATES.FUNDING_INVITE
177+ value: TRANSACTION_STATES.FUNDING_INVITE,
167 });
178 });
168 let usedTank = await tank.ensureFundsFor(
179 let usedTank = await tank.ensureFundsFor(
169− web3, point, inviteAddress, transferCost, [rawTransferStx], askForFunding
180+ web3,
181+ point,
182+ inviteAddress,
183+ transferCost,
184+ [rawTransferStx],
185+ askForFunding
170 );
186 );
171
187
172 updateProgress({
188 updateProgress({
173− type: "progress",
189+ type: 'progress',
174− value: TRANSACTION_STATES.CLAIMING
190+ value: TRANSACTION_STATES.CLAIMING,
175 });
191 });
176
192
177 // send transaction
193 // send transaction
@@ -186,7 +202,11 @@ async function startTransactions(args) {
186
202
187 // configure management proxy
203 // configure management proxy
188
204
189− let managementTx = azimuth.ecliptic.setManagementProxy(contracts, point, realWallet.management.keys.address);
205+ let managementTx = azimuth.ecliptic.setManagementProxy(
206+ contracts,
207+ point,
208+ realWallet.management.keys.address
209+ );
190 managementTx.gas = 200000;
210 managementTx.gas = 200000;
191 managementTx.nonce = 0;
211 managementTx.nonce = 0;
192
212
@@ -196,7 +216,7 @@ async function startTransactions(args) {
196 walletType: WALLET_NAMES.TICKET,
216 walletType: WALLET_NAMES.TICKET,
197 urbitWallet: Just(realWallet),
217 urbitWallet: Just(realWallet),
198 pointCursor: Just(point),
218 pointCursor: Just(point),
199− pointCache: {[point]: {keyRevisionNumber: 0}}
219+ pointCache: { [point]: { keyRevisionNumber: 0 } },
200 });
220 });
201 if (Nothing.hasInstance(networkSeed)) {
221 if (Nothing.hasInstance(networkSeed)) {
202 throw new Error('wtf network seed not derived');
222 throw new Error('wtf network seed not derived');
@@ -221,34 +241,39 @@ async function startTransactions(args) {
221 txs = txs.map(tx => {
241 txs = txs.map(tx => {
222 tx.gasPrice = 20000000000;
242 tx.gasPrice = 20000000000;
223 tx.from = newAddress;
243 tx.from = newAddress;
224− totalCost = totalCost + (tx.gasPrice * tx.gas);
244+ totalCost = totalCost + tx.gasPrice * tx.gas;
225 return tx;
245 return tx;
226 });
246 });
227
247
228 let rawStxs = txs.map(tx => {
248 let rawStxs = txs.map(tx => {
229 let stx = new Tx(tx);
249 let stx = new Tx(tx);
230 stx.sign(Buffer.from(realWallet.ownership.keys.private, 'hex'));
250 stx.sign(Buffer.from(realWallet.ownership.keys.private, 'hex'));
231− return '0x'+stx.serialize().toString('hex');
251+ return '0x' + stx.serialize().toString('hex');
232 });
252 });
233
253
234 updateProgress({
254 updateProgress({
235− type: "progress",
255+ type: 'progress',
236− value: TRANSACTION_STATES.FUNDING_RECIPIENT
256+ value: TRANSACTION_STATES.FUNDING_RECIPIENT,
237 });
257 });
238 usedTank = await tank.ensureFundsFor(
258 usedTank = await tank.ensureFundsFor(
259+ web3,
260+ point,
261+ newAddress,
262+ totalCost,
263+ rawStxs,
239− web3, point, newAddress, totalCost, rawStxs, askForFunding
264+ askForFunding
240 );
265 );
241
266
242 updateProgress({
267 updateProgress({
243− type: "progress",
268+ type: 'progress',
244− value: TRANSACTION_STATES.CONFIGURING
269+ value: TRANSACTION_STATES.CONFIGURING,
245 });
270 });
246
271
247 await sendAndAwaitConfirm(web3, rawStxs, usedTank);
272 await sendAndAwaitConfirm(web3, rawStxs, usedTank);
248
273
249 updateProgress({
274 updateProgress({
250− type: "progress",
275+ type: 'progress',
251− value: TRANSACTION_STATES.CLEANING
276+ value: TRANSACTION_STATES.CLEANING,
252 });
277 });
253
278
254 // if non-trivial eth left in invite wallet, transfer to new ownership
279 // if non-trivial eth left in invite wallet, transfer to new ownership
@@ -257,18 +282,18 @@ async function startTransactions(args) {
257 const gasLimit = 21000;
282 const gasLimit = 21000;
258 const sendEthCost = gasPrice * gasLimit;
283 const sendEthCost = gasPrice * gasLimit;
259 if (balance > sendEthCost) {
284 if (balance > sendEthCost) {
260− const value = (balance - sendEthCost);
285+ const value = balance - sendEthCost;
261 console.log('sending', value);
286 console.log('sending', value);
262 const tx = {
287 const tx = {
263 to: newAddress,
288 to: newAddress,
264 value: value,
289 value: value,
265 gasPrice: gasPrice,
290 gasPrice: gasPrice,
266 gas: gasLimit,
291 gas: gasLimit,
267− nonce: inviteNonce++
292+ nonce: inviteNonce++,
268− }
293+ };
269 let stx = new Tx(tx);
294 let stx = new Tx(tx);
270 stx.sign(inviteWallet.privateKey);
295 stx.sign(inviteWallet.privateKey);
271− const rawTx = '0x'+stx.serialize().toString('hex');
296+ const rawTx = '0x' + stx.serialize().toString('hex');
272 web3.eth.sendSignedTransaction(rawTx).catch(err => {
297 web3.eth.sendSignedTransaction(rawTx).catch(err => {
273 console.log('error sending value tx, who cares', err);
298 console.log('error sending value tx, who cares', err);
274 });
299 });
@@ -276,8 +301,8 @@ async function startTransactions(args) {
276 }
301 }
277
302
278 updateProgress({
303 updateProgress({
279− type: "progress",
304+ type: 'progress',
280− value: TRANSACTION_STATES.DONE
305+ value: TRANSACTION_STATES.DONE,
281 });
306 });
282
307
283 // proceed without waiting for confirm
308 // proceed without waiting for confirm
@@ -285,11 +310,13 @@ async function startTransactions(args) {
285}
310}
286
311
287async function sendAndAwaitConfirm(web3, signedTxs, usedTank) {
312async function sendAndAwaitConfirm(web3, signedTxs, usedTank) {
288− await Promise.all(signedTxs.map(tx => {
313+ await Promise.all(
314+ signedTxs.map(tx => {
289− return new Promise((resolve, reject) => {
315+ return new Promise((resolve, reject) => {
290− sendSignedTransaction(web3, tx, usedTank, resolve);
316+ sendSignedTransaction(web3, tx, usedTank, resolve);
291− });
317+ });
292− }));
318+ })
319+ );
293}
320}
294
321
295export {
322export {
@@ -298,5 +325,5 @@ export {
298 startTransactions,
325 startTransactions,
299 INVITE_STAGES,
326 INVITE_STAGES,
300 WALLET_STATES,
327 WALLET_STATES,
301− TRANSACTION_STATES
328+ TRANSACTION_STATES,
302−}
329+};
src/bridge/lib/inviteMail.js+13−17
@@ -2,7 +2,7 @@
2// did not succeed", you might need to visit localhost:3001 or whatever
2// did not succeed", you might need to visit localhost:3001 or whatever
3// explicitly and tell your browser that's safe to access.
3// explicitly and tell your browser that's safe to access.
4// https://stackoverflow.com/a/53011185/1334324
4// https://stackoverflow.com/a/53011185/1334324
5−const baseUrl = 'https://localhost:3002'
5+const baseUrl = 'https://localhost:3002';
6
6
7function sendRequest(where, what) {
7function sendRequest(where, what) {
8 return new Promise((resolve, reject) => {
8 return new Promise((resolve, reject) => {
@@ -10,23 +10,23 @@ function sendRequest(where, what) {
10 method: 'POST',
10 method: 'POST',
11 cache: 'no-cache',
11 cache: 'no-cache',
12 headers: {
12 headers: {
13− 'Content-Type': 'application/json'
13+ 'Content-Type': 'application/json',
14 },
14 },
15− body: JSON.stringify(what)
15+ body: JSON.stringify(what),
16 })
16 })
17− .then(response => {
17+ .then(response => {
18− if (response.ok) {
18+ if (response.ok) {
19− resolve(response.json());
19+ resolve(response.json());
20− } else {
20+ } else {
21− reject(response);
21+ reject(response);
22− }
22+ }
23− })
23+ })
24− .catch(reject);
24+ .catch(reject);
25 });
25 });
26}
26}
27
27
28async function hasReceived(recipient) {
28async function hasReceived(recipient) {
29− const res = await sendRequest('/has-received', {recipient});
29+ const res = await sendRequest('/has-received', { recipient });
30 return res.hasReceived;
30 return res.hasReceived;
31}
31}
32
32
@@ -34,8 +34,4 @@ function sendMail(recipient, ticket, tx) {
34 return sendRequest('/send-ticket', { recipient, ticket, tx });
34 return sendRequest('/send-ticket', { recipient, ticket, tx });
35}
35}
36
36
37−export {
38− hasReceived,
37+export { hasReceived, sendMail };
39− sendMail
40−}
41−
src/bridge/lib/keys.js+57−65
@@ -1,25 +1,22 @@
1−import BN from 'bn.js'
1+import BN from 'bn.js';
2−import Maybe from 'folktale/maybe'
2+import Maybe from 'folktale/maybe';
3−import * as need from '../lib/need'
3+import * as need from '../lib/need';
4
4
5−import * as noun from '../nockjs/noun'
5+import * as noun from '../nockjs/noun';
6−import * as serial from '../nockjs/serial'
6+import * as serial from '../nockjs/serial';
7−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
7+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
8
8
9−import { BRIDGE_ERROR } from './error'
9+import { BRIDGE_ERROR } from './error';
10−import {
11− WALLET_NAMES,
10+import { WALLET_NAMES, eqAddr } from './wallet';
12− eqAddr
13− } from './wallet'
14
11
15// ctsy joemfb
12// ctsy joemfb
16const b64 = buf => {
13const b64 = buf => {
17− let hex = buf.reverse().toString('hex')
14+ let hex = buf.reverse().toString('hex');
18− let n = new BN(hex, 'hex')
15+ let n = new BN(hex, 'hex');
19− let c = []
16+ let c = [];
20 while (1 === n.cmpn(0)) {
17 while (1 === n.cmpn(0)) {
21− c.push(n.andln(0x3f))
18+ c.push(n.andln(0x3f));
22− n = n.shrn(6)
19+ n = n.shrn(6);
23 }
20 }
24
21
25 // prettier-ignore
22 // prettier-ignore
@@ -34,38 +31,44 @@ const b64 = buf => {
34 ? 45
31 ? 45
35 : 126
32 : 126
36
33
37− return '0w' +
34+ return (
35+ '0w' +
38− c.reduce((a, b, i) =>
36+ c.reduce(
37+ (a, b, i) =>
39− String.fromCharCode(trans(b)) +
38+ String.fromCharCode(trans(b)) + (i && 0 === i % 5 ? '.' : '') + a,
40− (( i && 0 === i % 5 ) ? '.' : '') + a,
41− '')
39+ ''
40+ )
41+ );
42−}
42+};
43
43
44const jam = seed => {
44const jam = seed => {
45− const hex = serial.jam(seed).toString().slice(2)
45+ const hex = serial
46+ .jam(seed)
47+ .toString()
48+ .slice(2);
46− const pad = hex.length % 2 === 0 ? hex : '0' + hex
49+ const pad = hex.length % 2 === 0 ? hex : '0' + hex;
47− return Buffer.from(pad, 'hex').reverse()
50+ return Buffer.from(pad, 'hex').reverse();
48−}
51+};
49
52
50const genKey = (networkSeed, point, revision) => {
53const genKey = (networkSeed, point, revision) => {
51− const pair = kg.deriveNetworkKeys(networkSeed)
54+ const pair = kg.deriveNetworkKeys(networkSeed);
52− const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex') // '42' is curve parameter
55+ const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex'); // '42' is curve parameter
53
56
54 const sed = noun.dwim(
57 const sed = noun.dwim(
55 noun.Atom.fromString(point),
58 noun.Atom.fromString(point),
56 noun.Atom.fromInt(revision),
59 noun.Atom.fromInt(revision),
57 noun.Atom.fromString(bnsec.toString()),
60 noun.Atom.fromString(bnsec.toString()),
58 noun.Atom.yes
61 noun.Atom.yes
59− )
62+ );
60
63
61− return b64(jam(sed))
64+ return b64(jam(sed));
62−}
65+};
63
66
64// 'next' refers to setting the next set of keys
67// 'next' refers to setting the next set of keys
65// if false, we use revision - 1
68// if false, we use revision - 1
66const attemptSeedDerivation = async (next, args) => {
69const attemptSeedDerivation = async (next, args) => {
67− const { walletType } = args
70+ const { walletType } = args;
68− const { urbitWallet, authMnemonic } = args
71+ const { urbitWallet, authMnemonic } = args;
69
72
70 // NB (jtobin):
73 // NB (jtobin):
71 //
74 //
@@ -75,64 +78,53 @@ const attemptSeedDerivation = async (next, args) => {
75 const pointDetails = need.fromPointCache(args, point);
78 const pointDetails = need.fromPointCache(args, point);
76
79
77 const revision =
80 const revision =
78− next === true
81+ next === true
79− ? parseInt(pointDetails.keyRevisionNumber)
82+ ? parseInt(pointDetails.keyRevisionNumber)
80− : parseInt(pointDetails.keyRevisionNumber) - 1
83+ : parseInt(pointDetails.keyRevisionNumber) - 1;
81
84
82− let managementSeed = ''
85+ let managementSeed = '';
83
86
84− const ticketLike = [ WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS ]
87+ const ticketLike = [WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS];
85
88
86 if (ticketLike.includes(walletType)) {
89 if (ticketLike.includes(walletType)) {
87−
88 const uwal = urbitWallet.matchWith({
90 const uwal = urbitWallet.matchWith({
89 Just: uw => uw.value,
91 Just: uw => uw.value,
90 Nothing: _ => {
92 Nothing: _ => {
91− throw BRIDGE_ERROR.MISSING_URBIT_WALLET
93+ throw BRIDGE_ERROR.MISSING_URBIT_WALLET;
92− }
94+ },
93− })
95+ });
94−
95− managementSeed = uwal.management.seed
96
96
97+ managementSeed = uwal.management.seed;
97 } else if (walletType === WALLET_NAMES.MNEMONIC) {
98 } else if (walletType === WALLET_NAMES.MNEMONIC) {
98−
99 const walProxy = need.address(args);
99 const walProxy = need.address(args);
100
100
101 const mnemonic = authMnemonic.matchWith({
101 const mnemonic = authMnemonic.matchWith({
102 Just: mnem => mnem.value,
102 Just: mnem => mnem.value,
103 Nothing: _ => {
103 Nothing: _ => {
104− throw BRIDGE_ERROR.MISSING_MNEMONIC
104+ throw BRIDGE_ERROR.MISSING_MNEMONIC;
105− }
105+ },
106− })
106+ });
107
107
108− const chainProxy = pointDetails.managementProxy
108+ const chainProxy = pointDetails.managementProxy;
109
109
110 // the network seed is only derivable from mnemonic if the derived
110 // the network seed is only derivable from mnemonic if the derived
111 // management seed equals the record we have on chain
111 // management seed equals the record we have on chain
112− const networkSeedDerivable = eqAddr(walProxy, chainProxy)
112+ const networkSeedDerivable = eqAddr(walProxy, chainProxy);
113
113
114 if (networkSeedDerivable === true) {
114 if (networkSeedDerivable === true) {
115− managementSeed = mnemonic
115+ managementSeed = mnemonic;
116 }
116 }
117 }
117 }
118
118
119− let networkSeed = Maybe.Nothing()
119+ let networkSeed = Maybe.Nothing();
120
120
121 if (managementSeed !== '') {
121 if (managementSeed !== '') {
122− const seed =
123− await kg.deriveNetworkSeed(managementSeed, '', revision)
122+ const seed = await kg.deriveNetworkSeed(managementSeed, '', revision);
124
123
125− networkSeed = Maybe.Just(seed)
124+ networkSeed = Maybe.Just(seed);
126 }
125 }
127
126
128− return networkSeed
127+ return networkSeed;
129−}
130−
131−
132−
133−
134−export {
135− genKey,
136− attemptSeedDerivation
137−}
128+};
138
129
130+export { genKey, attemptSeedDerivation };
src/bridge/lib/ledger.js+16−23
@@ -1,31 +1,24 @@
1−import Transport from '@ledgerhq/hw-transport-u2f'
1+import Transport from '@ledgerhq/hw-transport-u2f';
2−import Eth from '@ledgerhq/hw-app-eth'
2+import Eth from '@ledgerhq/hw-app-eth';
3
3
4−const LEDGER_LEGACY_PATH = "m/44'/60'/0'/x"
4+const LEDGER_LEGACY_PATH = "m/44'/60'/0'/x";
5−const LEDGER_LIVE_PATH = "m/44'/60'/x'/0/0"
5+const LEDGER_LIVE_PATH = "m/44'/60'/x'/0/0";
6
6
7−const chopHdPrefix = str =>
7+const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
8− str.slice(0, 2) === "m/"
9− ? str.slice(2)
10− : str
11
8
12const ledgerSignTransaction = async (txn, hdpath) => {
9const ledgerSignTransaction = async (txn, hdpath) => {
13− const transport = await Transport.create()
10+ const transport = await Transport.create();
14− const eth = new Eth(transport)
11+ const eth = new Eth(transport);
15
12
16− const path = chopHdPrefix(hdpath)
13+ const path = chopHdPrefix(hdpath);
17− const serializedTx = txn.serialize().toString('hex')
14+ const serializedTx = txn.serialize().toString('hex');
18− const sig = await eth.signTransaction(path, serializedTx)
15+ const sig = await eth.signTransaction(path, serializedTx);
19
16
20− txn.v = Buffer.from(sig.v, 'hex')
17+ txn.v = Buffer.from(sig.v, 'hex');
21− txn.r = Buffer.from(sig.r, 'hex')
18+ txn.r = Buffer.from(sig.r, 'hex');
22− txn.s = Buffer.from(sig.s, 'hex')
19+ txn.s = Buffer.from(sig.s, 'hex');
23
20
24− return txn
21+ return txn;
25−}
22+};
26
23
27−export {
28− LEDGER_LIVE_PATH,
29− LEDGER_LEGACY_PATH,
30− ledgerSignTransaction
24+export { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH, ledgerSignTransaction };
31−}
src/bridge/lib/lib.js+19−18
@@ -1,6 +1,7 @@
1−import * as ob from 'urbit-ob'
1+import * as ob from 'urbit-ob';
2
2
3+const compose = (...fs) =>
3−const compose = (...fs) => fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v)
4+ fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v);
4
5
5const allFalse = (...args) => args.every(a => a === false);
6const allFalse = (...args) => args.every(a => a === false);
6
7
@@ -8,7 +9,7 @@ const isLast = (l, i) => i === l - 1;
8
9
9const isUndefined = v => v === undefined;
10const isUndefined = v => v === undefined;
10
11
11−const defaultTo = (v, d) => isUndefined(v) ? d : v;
12+const defaultTo = (v, d) => (isUndefined(v) ? d : v);
12
13
13const seq = n => Array.from(Array(n), (_, i) => i);
14const seq = n => Array.from(Array(n), (_, i) => i);
14
15
@@ -20,23 +21,23 @@ const t = v => v === true;
20const f = v => v === false;
21const f = v => v === false;
21
22
22const isValidGalaxy = name => {
23const isValidGalaxy = name => {
23− let point
24+ let point;
24 try {
25 try {
25− point = parseInt(ob.patp2dec(name), 10)
26+ point = parseInt(ob.patp2dec(name), 10);
26− } catch(err) {
27+ } catch (err) {
27− return false
28+ return false;
28 }
29 }
29− return point >= 0 && point < 256
30+ return point >= 0 && point < 256;
30−}
31+};
31
32
32−const randomPatq = (len) => {
33+const randomPatq = len => {
33− let bytes = window.crypto.getRandomValues(new Uint8Array(len))
34+ let bytes = window.crypto.getRandomValues(new Uint8Array(len));
34− let hex = bytes.reduce((acc, byt) =>
35+ let hex = bytes.reduce(
35− acc + byt.toString(16).padStart(2, '0'),
36+ (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
36 ''
37 ''
37− )
38+ );
38− return ob.hex2patq(hex)
39+ return ob.hex2patq(hex);
39−}
40+};
40
41
41export {
42export {
42 compose,
43 compose,
@@ -49,5 +50,5 @@ export {
49 t,
50 t,
50 f,
51 f,
51 isValidGalaxy,
52 isValidGalaxy,
52− randomPatq
53+ randomPatq,
53−}
54+};
src/bridge/lib/need.js+42−29
@@ -2,32 +2,44 @@
2//
2//
3// generally, pass the props object into these functions
3// generally, pass the props object into these functions
4
4
5−import { BRIDGE_ERROR } from '../lib/error'
5+import { BRIDGE_ERROR } from '../lib/error';
6
6
7function web3(obj) {
7function web3(obj) {
8− return ('web3' in obj === false)
8+ return 'web3' in obj === false
9+ ? (() => {
9− ? (() => { throw BRIDGE_ERROR.MISSING_WEB3 })()
10+ throw BRIDGE_ERROR.MISSING_WEB3;
11+ })()
10 : obj.web3.matchWith({
12 : obj.web3.matchWith({
13+ Nothing: () => {
11− Nothing: () => { throw BRIDGE_ERROR.MISSING_WEB3 },
14+ throw BRIDGE_ERROR.MISSING_WEB3;
15+ },
12− Just: w => w.value
16+ Just: w => w.value,
13 });
17 });
14}
18}
15
19
16function contracts(obj) {
20function contracts(obj) {
17− return ('contracts' in obj === false)
21+ return 'contracts' in obj === false
22+ ? (() => {
18− ? (() => { throw BRIDGE_ERROR.MISSING_CONTRACTS })()
23+ throw BRIDGE_ERROR.MISSING_CONTRACTS;
24+ })()
19 : obj.contracts.matchWith({
25 : obj.contracts.matchWith({
26+ Nothing: () => {
20− Nothing: () => { throw BRIDGE_ERROR.MISSING_CONTRACTS },
27+ throw BRIDGE_ERROR.MISSING_CONTRACTS;
28+ },
21− Just: c => c.value
29+ Just: c => c.value,
22 });
30 });
23}
31}
24
32
25function wallet(obj) {
33function wallet(obj) {
26− return ('wallet' in obj === false)
34+ return 'wallet' in obj === false
35+ ? (() => {
27− ? (() => { throw BRIDGE_ERROR.MISSING_WALLET })()
36+ throw BRIDGE_ERROR.MISSING_WALLET;
37+ })()
28 : obj.wallet.matchWith({
38 : obj.wallet.matchWith({
39+ Nothing: () => {
29− Nothing: () => { throw BRIDGE_ERROR.MISSING_WALLET },
40+ throw BRIDGE_ERROR.MISSING_WALLET;
41+ },
30− Just: w => w.value
42+ Just: w => w.value,
31 });
43 });
32}
44}
33
45
@@ -36,27 +48,28 @@ function address(obj) {
36}
48}
37
49
38function pointCursor(obj) {
50function pointCursor(obj) {
39− return ('pointCursor' in obj === false)
51+ return 'pointCursor' in obj === false
52+ ? (() => {
40− ? (() => { throw BRIDGE_ERROR.MISSING_POINT })()
53+ throw BRIDGE_ERROR.MISSING_POINT;
54+ })()
41 : obj.pointCursor.matchWith({
55 : obj.pointCursor.matchWith({
56+ Nothing: () => {
42− Nothing: () => { throw BRIDGE_ERROR.MISSING_POINT },
57+ throw BRIDGE_ERROR.MISSING_POINT;
58+ },
43− Just: p => p.value
59+ Just: p => p.value,
44 });
60 });
45}
61}
46
62
47function fromPointCache(obj, point) {
63function fromPointCache(obj, point) {
48− return ('pointCache' in obj === false)
64+ return 'pointCache' in obj === false
65+ ? (() => {
49− ? (() => { throw BRIDGE_ERROR.MISSING_POINT })()
66+ throw BRIDGE_ERROR.MISSING_POINT;
67+ })()
50 : point in obj.pointCache
68 : point in obj.pointCache
51− ? obj.pointCache[point]
69+ ? obj.pointCache[point]
70+ : (() => {
52− : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
71+ throw BRIDGE_ERROR.MISSING_POINT;
72+ })();
53}
73}
54
74
55−export {
75+export { web3, contracts, wallet, address, pointCursor, fromPointCache };
56− web3,
57− contracts,
58− wallet,
59− address,
60− pointCursor,
61− fromPointCache
62−}
src/bridge/lib/network.js+12−15
@@ -3,20 +3,17 @@ const NETWORK_NAMES = {
3 LOCAL: Symbol('LOCAL'),
3 LOCAL: Symbol('LOCAL'),
4 ROPSTEN: Symbol('ROPSTEN'),
4 ROPSTEN: Symbol('ROPSTEN'),
5 MAINNET: Symbol('MAINNET'),
5 MAINNET: Symbol('MAINNET'),
6−}
6+};
7
7
8−const renderNetworkType = (network) =>
8+const renderNetworkType = network =>
9− network === NETWORK_NAMES.OFFLINE
9+ network === NETWORK_NAMES.OFFLINE
10− ? 'Offline'
10+ ? 'Offline'
11− : network === NETWORK_NAMES.ROPSTEN
11+ : network === NETWORK_NAMES.ROPSTEN
12− ? 'Ropsten'
12+ ? 'Ropsten'
13− : network === NETWORK_NAMES.MAINNET
13+ : network === NETWORK_NAMES.MAINNET
14− ? 'Main Network'
14+ ? 'Main Network'
15− : network === NETWORK_NAMES.LOCAL
15+ : network === NETWORK_NAMES.LOCAL
16− ? 'Local Node'
16+ ? 'Local Node'
17− : 'Offline'
17+ : 'Offline';
18
18
19−export {
20− NETWORK_NAMES,
21− renderNetworkType
19+export { NETWORK_NAMES, renderNetworkType };
22−}
src/bridge/lib/proxy.js+10−15
@@ -1,21 +1,16 @@
1−
2const PROXY_TYPE = {
1const PROXY_TYPE = {
3 MANAGEMENT_PROXY: Symbol('MANAGEMENT_PROXY'),
2 MANAGEMENT_PROXY: Symbol('MANAGEMENT_PROXY'),
4 SPAWN_PROXY: Symbol('SPAWN_PROXY'),
3 SPAWN_PROXY: Symbol('SPAWN_PROXY'),
5 TRANSFER_PROXY: Symbol('TRANSFER_PROXY'),
4 TRANSFER_PROXY: Symbol('TRANSFER_PROXY'),
6−}
5+};
7−
8−const renderProxyType = (proxyType) =>
9− proxyType === PROXY_TYPE.MANAGEMENT_PROXY
10− ? 'management'
11− : proxyType === PROXY_TYPE.SPAWN_PROXY
12− ? 'spawn'
13− : proxyType === PROXY_TYPE.TRANSFER_PROXY
14− ? 'transfer'
15− : 'proxy'
16
6
17−export {
18− PROXY_TYPE,
19− renderProxyType
7+const renderProxyType = proxyType =>
20−}
8+ proxyType === PROXY_TYPE.MANAGEMENT_PROXY
9+ ? 'management'
10+ : proxyType === PROXY_TYPE.SPAWN_PROXY
11+ ? 'spawn'
12+ : proxyType === PROXY_TYPE.TRANSFER_PROXY
13+ ? 'transfer'
14+ : 'proxy';
21
15
16+export { PROXY_TYPE, renderProxyType };
src/bridge/lib/router.js+84−115
@@ -1,43 +1,43 @@
1−import * as ob from 'urbit-ob'
1+import * as ob from 'urbit-ob';
2−import * as need from '../lib/need'
2+import * as need from '../lib/need';
3
3
4import React from 'react';
4import React from 'react';
5
5
6−import Landing from '../views/Landing'
6+import Landing from '../views/Landing';
7−
7+
8−import InviteTicket from '../views/InviteTicket'
8+import InviteTicket from '../views/InviteTicket';
9−
9+
10−import InvitesSend from '../views/InvitesSend.js'
10+import InvitesSend from '../views/InvitesSend.js';
11−import InvitesManage from '../views/InvitesManage.js'
11+import InvitesManage from '../views/InvitesManage.js';
12−
12+
13−import AcceptTransfer from '../views/AcceptTransfer'
13+import AcceptTransfer from '../views/AcceptTransfer';
14−import CancelTransfer from '../views/CancelTransfer'
14+import CancelTransfer from '../views/CancelTransfer';
15−import CreateGalaxy from '../views/CreateGalaxy'
15+import CreateGalaxy from '../views/CreateGalaxy';
16−import GenKeyfile from '../views/GenKeyfile'
16+import GenKeyfile from '../views/GenKeyfile';
17−import IssueChild from '../views/IssueChild'
17+import IssueChild from '../views/IssueChild';
18−import Network from '../views/Network'
18+import Network from '../views/Network';
19−import Mnemonic from '../views/Mnemonic'
19+import Mnemonic from '../views/Mnemonic';
20−import Ledger from '../views/Ledger'
20+import Ledger from '../views/Ledger';
21−import Trezor from '../views/Trezor'
21+import Trezor from '../views/Trezor';
22−import PrivateKey from '../views/PrivateKey'
22+import PrivateKey from '../views/PrivateKey';
23−import Keystore from '../views/Keystore'
23+import Keystore from '../views/Keystore';
24−import ViewPoint from '../views/ViewPoint'
24+import ViewPoint from '../views/ViewPoint';
25−import SentTransaction from '../views/SentTransaction'
25+import SentTransaction from '../views/SentTransaction';
26−import SetKeys from '../views/SetKeys'
26+import SetKeys from '../views/SetKeys';
27import {
27import {
28 SetManagementProxy,
28 SetManagementProxy,
29 SetSpawnProxy,
29 SetSpawnProxy,
30− SetTransferProxy
30+ SetTransferProxy,
31− } from '../views/SetProxy'
31+} from '../views/SetProxy';
32−import Point from '../views/Point'
32+import Point from '../views/Point';
33−import Points from '../views/Points'
33+import Points from '../views/Points';
34−import Shards from '../views/Shards'
34+import Shards from '../views/Shards';
35−import Ticket from '../views/Ticket'
35+import Ticket from '../views/Ticket';
36−import Transfer from '../views/Transfer'
36+import Transfer from '../views/Transfer';
37−import Wallet from '../views/Wallet'
37+import Wallet from '../views/Wallet';
38−
38+
39−import { EthereumWallet } from './wallet'
39+import { EthereumWallet } from './wallet';
40−import { renderNetworkType } from './network'
40+import { renderNetworkType } from './network';
41
41
42const ROUTE_NAMES = {
42const ROUTE_NAMES = {
43 DEFAULT: Symbol('DEFAULT'),
43 DEFAULT: Symbol('DEFAULT'),
@@ -70,144 +70,113 @@ const ROUTE_NAMES = {
70 SET_KEYS: Symbol('SET_KEYS'),
70 SET_KEYS: Symbol('SET_KEYS'),
71 TRANSFER: Symbol('TRANSFER'),
71 TRANSFER: Symbol('TRANSFER'),
72 SENT_TRANSACTION: Symbol('SENT_TRANSACTION'),
72 SENT_TRANSACTION: Symbol('SENT_TRANSACTION'),
73− GEN_KEYFILE: Symbol('GEN_KEYFILE')
73+ GEN_KEYFILE: Symbol('GEN_KEYFILE'),
74−}
74+};
75
75
76const createRoutes = () => {
76const createRoutes = () => {
77− const routes = {}
77+ const routes = {};
78− routes[ROUTE_NAMES.DEFAULT] = Landing
78+ routes[ROUTE_NAMES.DEFAULT] = Landing;
79 //
79 //
80− routes[ROUTE_NAMES.INVITE_TICKET] = InviteTicket
80+ routes[ROUTE_NAMES.INVITE_TICKET] = InviteTicket;
81− routes[ROUTE_NAMES.INVITES_SEND] = InvitesSend
81+ routes[ROUTE_NAMES.INVITES_SEND] = InvitesSend;
82− routes[ROUTE_NAMES.INVITES_MANAGE] = InvitesManage
82+ routes[ROUTE_NAMES.INVITES_MANAGE] = InvitesManage;
83 //
83 //
84− routes[ROUTE_NAMES.ACCEPT_TRANSFER] = AcceptTransfer
84+ routes[ROUTE_NAMES.ACCEPT_TRANSFER] = AcceptTransfer;
85− routes[ROUTE_NAMES.CANCEL_TRANSFER] = CancelTransfer
85+ routes[ROUTE_NAMES.CANCEL_TRANSFER] = CancelTransfer;
86− routes[ROUTE_NAMES.LANDING] = Landing
86+ routes[ROUTE_NAMES.LANDING] = Landing;
87− routes[ROUTE_NAMES.NETWORK] = Network
87+ routes[ROUTE_NAMES.NETWORK] = Network;
88− routes[ROUTE_NAMES.WALLET] = Wallet
88+ routes[ROUTE_NAMES.WALLET] = Wallet;
89− routes[ROUTE_NAMES.VIEW_SHIP] = ViewPoint
89+ routes[ROUTE_NAMES.VIEW_SHIP] = ViewPoint;
90− routes[ROUTE_NAMES.MNEMONIC] = Mnemonic
90+ routes[ROUTE_NAMES.MNEMONIC] = Mnemonic;
91− routes[ROUTE_NAMES.TICKET] = Ticket
91+ routes[ROUTE_NAMES.TICKET] = Ticket;
92− routes[ROUTE_NAMES.SHARDS] = Shards
92+ routes[ROUTE_NAMES.SHARDS] = Shards;
93− routes[ROUTE_NAMES.LEDGER] = Ledger
93+ routes[ROUTE_NAMES.LEDGER] = Ledger;
94− routes[ROUTE_NAMES.TREZOR] = Trezor
94+ routes[ROUTE_NAMES.TREZOR] = Trezor;
95− routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey
95+ routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey;
96− routes[ROUTE_NAMES.KEYSTORE] = Keystore
96+ routes[ROUTE_NAMES.KEYSTORE] = Keystore;
97− routes[ROUTE_NAMES.SHIPS] = Points
97+ routes[ROUTE_NAMES.SHIPS] = Points;
98− routes[ROUTE_NAMES.SHIP] = Point
98+ routes[ROUTE_NAMES.SHIP] = Point;
99− routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy
99+ routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy;
100− routes[ROUTE_NAMES.SET_SPAWN_PROXY] = SetSpawnProxy
100+ routes[ROUTE_NAMES.SET_SPAWN_PROXY] = SetSpawnProxy;
101− routes[ROUTE_NAMES.SET_TRANSFER_PROXY] = SetTransferProxy
101+ routes[ROUTE_NAMES.SET_TRANSFER_PROXY] = SetTransferProxy;
102− routes[ROUTE_NAMES.CREATE_GALAXY] = CreateGalaxy
102+ routes[ROUTE_NAMES.CREATE_GALAXY] = CreateGalaxy;
103− routes[ROUTE_NAMES.ISSUE_CHILD] = IssueChild
103+ routes[ROUTE_NAMES.ISSUE_CHILD] = IssueChild;
104− routes[ROUTE_NAMES.SET_KEYS] = SetKeys
104+ routes[ROUTE_NAMES.SET_KEYS] = SetKeys;
105− routes[ROUTE_NAMES.TRANSFER] = Transfer
105+ routes[ROUTE_NAMES.TRANSFER] = Transfer;
106− routes[ROUTE_NAMES.SENT_TRANSACTION] = SentTransaction
106+ routes[ROUTE_NAMES.SENT_TRANSACTION] = SentTransaction;
107− routes[ROUTE_NAMES.GEN_KEYFILE] = GenKeyfile
107+ routes[ROUTE_NAMES.GEN_KEYFILE] = GenKeyfile;
108− return routes
108+ return routes;
109−}
109+};
110−
110+
111−const ROUTES = createRoutes()
111+const ROUTES = createRoutes();
112
112
113const renderRoute = (props, route) => {
113const renderRoute = (props, route) => {
114− const { wallet, networkType } = props
114+ const { wallet, networkType } = props;
115− return (
116− route === ROUTE_NAMES.LANDING
115+ return route === ROUTE_NAMES.LANDING
117 ? 'Bridge'
116 ? 'Bridge'
118−
119 : route === ROUTE_NAMES.INVITE_TICKET
117 : route === ROUTE_NAMES.INVITE_TICKET
120 ? 'Invite code'
118 ? 'Invite code'
121−
122 : route === ROUTE_NAMES.INVITE_TRANSACTIONS
119 : route === ROUTE_NAMES.INVITE_TRANSACTIONS
123 ? 'Setting up new wallet'
120 ? 'Setting up new wallet'
124−
125 : route === ROUTE_NAMES.INVITES_SEND
121 : route === ROUTE_NAMES.INVITES_SEND
126 ? 'Send invites'
122 ? 'Send invites'
127−
128 : route === ROUTE_NAMES.INVITES_MANAGE
123 : route === ROUTE_NAMES.INVITES_MANAGE
129 ? 'Manage invites'
124 ? 'Manage invites'
130−
131 : route === ROUTE_NAMES.NETWORK
125 : route === ROUTE_NAMES.NETWORK
132 ? `${renderNetworkType(networkType)}`
126 ? `${renderNetworkType(networkType)}`
133−
134 : route === ROUTE_NAMES.WALLET
127 : route === ROUTE_NAMES.WALLET
135 ? wallet.matchWith({
128 ? wallet.matchWith({
136 Nothing: () => 'Wallet',
129 Nothing: () => 'Wallet',
137− Just: (wal) =>
130+ Just: wal =>
138− wal.value instanceof EthereumWallet
131+ wal.value instanceof EthereumWallet ? (
139− ? (<span className="text-mono">{wal.value.address}</span>)
132+ <span className="text-mono">{wal.value.address}</span>
133+ ) : (
140− : (<span className="text-mono">{wal.value.address}</span>)
134+ <span className="text-mono">{wal.value.address}</span>
135+ ),
141 })
136 })
142−
143 : route === ROUTE_NAMES.MNEMONIC
137 : route === ROUTE_NAMES.MNEMONIC
144 ? 'Mnemonic'
138 ? 'Mnemonic'
145−
146 : route === ROUTE_NAMES.TICKET
139 : route === ROUTE_NAMES.TICKET
147 ? 'Urbit Ticket'
140 ? 'Urbit Ticket'
148−
149 : route === ROUTE_NAMES.SHARDS
141 : route === ROUTE_NAMES.SHARDS
150 ? 'Urbit Ticket'
142 ? 'Urbit Ticket'
151−
152 : route === ROUTE_NAMES.LEDGER
143 : route === ROUTE_NAMES.LEDGER
153 ? 'Ledger'
144 ? 'Ledger'
154−
155 : route === ROUTE_NAMES.TREZOR
145 : route === ROUTE_NAMES.TREZOR
156 ? 'Trezor'
146 ? 'Trezor'
157−
158 : route === ROUTE_NAMES.PRIVATE_KEY
147 : route === ROUTE_NAMES.PRIVATE_KEY
159 ? 'Private Key'
148 ? 'Private Key'
160−
161 : route === ROUTE_NAMES.KEYSTORE
149 : route === ROUTE_NAMES.KEYSTORE
162 ? 'Keystore File'
150 ? 'Keystore File'
163−
164 : route === ROUTE_NAMES.SHIPS
151 : route === ROUTE_NAMES.SHIPS
165 ? 'Points'
152 ? 'Points'
166−
167 : route === ROUTE_NAMES.VIEW_SHIP
153 : route === ROUTE_NAMES.VIEW_SHIP
168 ? 'View'
154 ? 'View'
169−
170 : route === ROUTE_NAMES.SHIP
155 : route === ROUTE_NAMES.SHIP
171 ? ob.patp(need.pointCursor(props))
156 ? ob.patp(need.pointCursor(props))
172−
173− : route === ROUTE_NAMES.SET_TRANSFER_PROXY
157+ : route === ROUTE_NAMES.SET_TRANSFER_PROXY ||
174− || route === ROUTE_NAMES.SET_MANAGEMENT_PROXY
158+ route === ROUTE_NAMES.SET_MANAGEMENT_PROXY ||
175− || route === ROUTE_NAMES.SET_SPAWN_PROXY
159+ route === ROUTE_NAMES.SET_SPAWN_PROXY
176 ? 'Set Proxy'
160 ? 'Set Proxy'
177−
178 : route === ROUTE_NAMES.CREATE_GALAXY
161 : route === ROUTE_NAMES.CREATE_GALAXY
179 ? 'Create Galaxy'
162 ? 'Create Galaxy'
180−
181 : route === ROUTE_NAMES.ISSUE_CHILD
163 : route === ROUTE_NAMES.ISSUE_CHILD
182 ? 'Issue Child'
164 ? 'Issue Child'
183−
184 : route === ROUTE_NAMES.TRANSFER
165 : route === ROUTE_NAMES.TRANSFER
185 ? 'Transfer'
166 ? 'Transfer'
186−
187 : route === ROUTE_NAMES.ACCEPT_TRANSFER
167 : route === ROUTE_NAMES.ACCEPT_TRANSFER
188 ? 'Accept Transfer'
168 ? 'Accept Transfer'
189−
190 : route === ROUTE_NAMES.CANCEL_TRANSFER
169 : route === ROUTE_NAMES.CANCEL_TRANSFER
191 ? 'Cancel Transfer'
170 ? 'Cancel Transfer'
192−
193 : route === ROUTE_NAMES.GEN_KEYFILE
171 : route === ROUTE_NAMES.GEN_KEYFILE
194 ? 'Keyfile'
172 ? 'Keyfile'
195−
196 : route === ROUTE_NAMES.SET_KEYS
173 : route === ROUTE_NAMES.SET_KEYS
197 ? 'Configure Keys'
174 ? 'Configure Keys'
198−
199 : route === ROUTE_NAMES.SENT_TRANSACTION
175 : route === ROUTE_NAMES.SENT_TRANSACTION
200 ? 'Txn'
176 ? 'Txn'
177+ : 'Bridge';
178+};
201
179
202− : 'Bridge'
203− )
204−}
205−
206−const router = (route) =>
180+const router = route => ROUTES[route];
207− ROUTES[route]
208
181
209−export {
210− ROUTE_NAMES,
182+export { ROUTE_NAMES, router, renderRoute };
211− router,
212− renderRoute
213−}
src/bridge/lib/state.js+5−17
@@ -1,48 +1,36 @@
1−
2−
3const invert = (key, state) => {
1const invert = (key, state) => {
4 return {
2 return {
5 [key]: !state[key],
3 [key]: !state[key],
6− }
4+ };
7};
5};
8
6
9−
10// const invert = (state, key) => {
7// const invert = (state, key) => {
11// return {
8// return {
12// [key]: !state[key],
9// [key]: !state[key],
13// }
10// }
14// };
11// };
15
12
16−
17const toTrue = key => {
13const toTrue = key => {
18 return {
14 return {
19 [key]: true,
15 [key]: true,
20− }
16+ };
21};
17};
22
18
23const toFalse = key => {
19const toFalse = key => {
24 return {
20 return {
25 [key]: false,
21 [key]: false,
26− }
22+ };
27};
23};
28
24
29const toNull = key => {
25const toNull = key => {
30 return {
26 return {
31 [key]: false,
27 [key]: false,
32− }
28+ };
33};
29};
34
30
35−
36// const chain = (context, key, data, ...fs) => {
31// const chain = (context, key, data, ...fs) => {
37// fs.forEach(f => f());
32// fs.forEach(f => f());
38// context.setState({[k]: data});
33// context.setState({[k]: data});
39// };
34// };
40
35
41−
36+export { invert, toTrue, toFalse, toNull };
42−
43−export {
44− invert,
45− toTrue,
46− toFalse,
47− toNull,
48−}
src/bridge/lib/tank.js+37−31
@@ -1,6 +1,6 @@
1// /lib/tank: functions for funding transactions
1// /lib/tank: functions for funding transactions
2
2
3−import { waitForTransactionConfirm } from './txn'
3+import { waitForTransactionConfirm } from './txn';
4
4
5//NOTE if accessing this in a localhost configuration fails with "CORS request
5//NOTE if accessing this in a localhost configuration fails with "CORS request
6// did not succeed", you might need to visit localhost:3001 or whatever
6// did not succeed", you might need to visit localhost:3001 or whatever
@@ -14,28 +14,28 @@ function sendRequest(where, what) {
14 method: 'POST',
14 method: 'POST',
15 cache: 'no-cache',
15 cache: 'no-cache',
16 headers: {
16 headers: {
17− 'Content-Type': 'application/json'
17+ 'Content-Type': 'application/json',
18 },
18 },
19− body: JSON.stringify(what)
19+ body: JSON.stringify(what),
20 })
20 })
21− .then(response => {
21+ .then(response => {
22− if (response.ok) {
22+ if (response.ok) {
23− resolve(response.json());
23+ resolve(response.json());
24− } else {
24+ } else {
25− reject(response);
25+ reject(response);
26− }
26+ }
27− })
27+ })
28− .catch(reject);
28+ .catch(reject);
29 });
29 });
30}
30}
31
31
32const remainingTransactions = point => {
32const remainingTransactions = point => {
33 if (typeof point === 'string') point = Number(point);
33 if (typeof point === 'string') point = Number(point);
34− return sendRequest('/point', {point:point});
34+ return sendRequest('/point', { point: point });
35};
35};
36
36
37const fundTransactions = signedTxs => {
37const fundTransactions = signedTxs => {
38− return sendRequest('/request', {txs:signedTxs});
38+ return sendRequest('/request', { txs: signedTxs });
39};
39};
40
40
41// resolves when address has at least cost balance
41// resolves when address has at least cost balance
@@ -43,14 +43,19 @@ const fundTransactions = signedTxs => {
43// askForFunding: callback that takes (address, requiredBalance, currentBalance)
43// askForFunding: callback that takes (address, requiredBalance, currentBalance)
44// and tells the user to get that address the required balance
44// and tells the user to get that address the required balance
45// gotFunding: optional callback that stops telling the user to go get funding
45// gotFunding: optional callback that stops telling the user to go get funding
46−const ensureFundsFor = async (web3, point, address, cost, signedTxs,
46+const ensureFundsFor = async (
47+ web3,
48+ point,
49+ address,
50+ cost,
51+ signedTxs,
47− askForFunding, gotFunding) => {
52+ askForFunding,
53+ gotFunding
54+) => {
48 let balance = await web3.eth.getBalance(address);
55 let balance = await web3.eth.getBalance(address);
49
56
50 if (cost > balance) {
57 if (cost > balance) {
51−
52 try {
58 try {
53−
54 if (point !== null) {
59 if (point !== null) {
55 const fundsRemaining = await remainingTransactions(point);
60 const fundsRemaining = await remainingTransactions(point);
56 if (fundsRemaining < signedTxs.length) {
61 if (fundsRemaining < signedTxs.length) {
@@ -69,17 +74,18 @@ const ensureFundsFor = async (web3, point, address, cost, signedTxs,
69 } else {
74 } else {
70 await waitForTransactionConfirm(web3, res.txHash);
75 await waitForTransactionConfirm(web3, res.txHash);
71 let newBalance = await web3.eth.getBalance(address);
76 let newBalance = await web3.eth.getBalance(address);
77+ console.log(
72− console.log('tank: funds have confirmed', balance >= cost, balance, newBalance);
78+ 'tank: funds have confirmed',
79+ balance >= cost,
80+ balance,
81+ newBalance
82+ );
73 return true;
83 return true;
74 }
84 }
75−
76 } catch (e) {
85 } catch (e) {
77−
78 console.log('tank: funding failed', e);
86 console.log('tank: funding failed', e);
79 await waitForBalance(web3, address, cost, askForFunding, gotFunding);
87 await waitForBalance(web3, address, cost, askForFunding, gotFunding);
80−
81 }
88 }
82−
83 } else {
89 } else {
84 console.log('tank: already have sufficient funds');
90 console.log('tank: already have sufficient funds');
85 }
91 }
@@ -89,8 +95,13 @@ const ensureFundsFor = async (web3, point, address, cost, signedTxs,
89// resolves when address has at least minBalance
95// resolves when address has at least minBalance
90//
96//
91//TODO should maybe do a "we got it" callback also, so clients can hide msg?
97//TODO should maybe do a "we got it" callback also, so clients can hide msg?
92−async function waitForBalance(web3, address, minBalance,
98+async function waitForBalance(
99+ web3,
100+ address,
101+ minBalance,
93− askForFunding, gotFunding) {
102+ askForFunding,
103+ gotFunding
104+) {
94 console.log('tank: awaiting balance', address, minBalance);
105 console.log('tank: awaiting balance', address, minBalance);
95 return new Promise((resolve, reject) => {
106 return new Promise((resolve, reject) => {
96 let oldBalance = null;
107 let oldBalance = null;
@@ -98,7 +109,7 @@ async function waitForBalance(web3, address, minBalance,
98 const balance = await web3.eth.getBalance(address);
109 const balance = await web3.eth.getBalance(address);
99 if (balance >= minBalance) {
110 if (balance >= minBalance) {
100 // if we ever asked for funding, retract that request now
111 // if we ever asked for funding, retract that request now
101− if (gotFunding && (oldBalance !== null)) {
112+ if (gotFunding && oldBalance !== null) {
102 gotFunding();
113 gotFunding();
103 }
114 }
104 resolve();
115 resolve();
@@ -113,9 +124,4 @@ async function waitForBalance(web3, address, minBalance,
113 });
124 });
114}
125}
115
126
116−export {
117− remainingTransactions,
127+export { remainingTransactions, fundTransactions, ensureFundsFor };
118− fundTransactions,
119− ensureFundsFor
120−}
121−
src/bridge/lib/transformers.js+2−3
@@ -1,6 +1,6 @@
1// import { fill } from './lib'
1// import { fill } from './lib'
2
2
3−const prependSig = s => s.charAt(0) !== '~' ? `~${s}` : s;
3+const prependSig = s => (s.charAt(0) !== '~' ? `~${s}` : s);
4
4
5// const hideAllButLast = s => {
5// const hideAllButLast = s => {
6// const ll = s[s.length -1];
6// const ll = s[s.length -1];
@@ -10,9 +10,8 @@ const prependSig = s => s.charAt(0) !== '~' ? `~${s}` : s;
10//
10//
11// const hideAll = s => fill(s.length, '•');
11// const hideAll = s => fill(s.length, '•');
12
12
13−
14export {
13export {
15 prependSig,
14 prependSig,
16 // hideAllButLast,
15 // hideAllButLast,
17 // hideAll,
16 // hideAll,
18−}
17+};
src/bridge/lib/trezor.js+14−20
@@ -1,11 +1,9 @@
1−import TrezorConnect from 'trezor-connect'
1+import TrezorConnect from 'trezor-connect';
2
2
3−const TREZOR_PATH = "m/44'/60'/0'/0/x"
3+const TREZOR_PATH = "m/44'/60'/0'/0/x";
4
4
5const formatChainId = val =>
5const formatChainId = val =>
6− typeof val === 'number'
7− ? val
8− : parseInt(val.slice(2), 16) // assume 0x-prefixed hex string
6+ typeof val === 'number' ? val : parseInt(val.slice(2), 16); // assume 0x-prefixed hex string
9
7
10const trezorSignTransaction = async (txn, hdpath) => {
8const trezorSignTransaction = async (txn, hdpath) => {
11 const trezorFormattedTxn = {
9 const trezorFormattedTxn = {
@@ -15,25 +13,21 @@ const trezorSignTransaction = async (txn, hdpath) => {
15 gasLimit: txn.gasLimit.toString('hex'),
13 gasLimit: txn.gasLimit.toString('hex'),
16 gasPrice: txn.gasPrice.toString('hex'),
14 gasPrice: txn.gasPrice.toString('hex'),
17 nonce: txn.nonce.length === 0 ? '00' : txn.nonce.toString('hex'),
15 nonce: txn.nonce.length === 0 ? '00' : txn.nonce.toString('hex'),
18− chainId: formatChainId(txn.getChainId())
16+ chainId: formatChainId(txn.getChainId()),
19− }
17+ };
20
18
21 const sig = await TrezorConnect.ethereumSignTransaction({
19 const sig = await TrezorConnect.ethereumSignTransaction({
22 path: hdpath,
20 path: hdpath,
23− transaction: trezorFormattedTxn
21+ transaction: trezorFormattedTxn,
24− })
22+ });
25
23
26− const payload = sig.payload
24+ const payload = sig.payload;
27
25
28− txn.v = Buffer.from(payload.v.slice(2), 'hex')
26+ txn.v = Buffer.from(payload.v.slice(2), 'hex');
29− txn.r = Buffer.from(payload.r.slice(2), 'hex')
27+ txn.r = Buffer.from(payload.r.slice(2), 'hex');
30− txn.s = Buffer.from(payload.s.slice(2), 'hex')
28+ txn.s = Buffer.from(payload.s.slice(2), 'hex');
31
29
32− return txn
30+ return txn;
33−}
31+};
34−
35−export {
36− TREZOR_PATH,
37− trezorSignTransaction
38−}
39
32
33+export { TREZOR_PATH, trezorSignTransaction };
src/bridge/lib/txn.js+121−123
@@ -1,16 +1,13 @@
1−import * as ob from 'urbit-ob'
1+import * as ob from 'urbit-ob';
2−import { Just } from 'folktale/maybe'
2+import { Just } from 'folktale/maybe';
3−import Tx from 'ethereumjs-tx'
3+import Tx from 'ethereumjs-tx';
4−import { toWei, fromWei, toHex } from 'web3-utils'
4+import { toWei, fromWei, toHex } from 'web3-utils';
5−
5+
6−import { BRIDGE_ERROR } from '../lib/error'
6+import { BRIDGE_ERROR } from '../lib/error';
7−import { NETWORK_NAMES } from '../lib/network'
7+import { NETWORK_NAMES } from '../lib/network';
8−import { ledgerSignTransaction } from '../lib/ledger'
8+import { ledgerSignTransaction } from '../lib/ledger';
9−import { trezorSignTransaction } from '../lib/trezor'
9+import { trezorSignTransaction } from '../lib/trezor';
10−import {
11− WALLET_NAMES,
12− addHexPrefix
13− } from '../lib/wallet'
10+import { WALLET_NAMES, addHexPrefix } from '../lib/wallet';
14
11
15const TXN_PURPOSE = {
12const TXN_PURPOSE = {
16 SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
13 SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
@@ -20,30 +17,29 @@ const TXN_PURPOSE = {
20 ISSUE_CHILD: Symbol('ISSUE_CHILD'),
17 ISSUE_CHILD: Symbol('ISSUE_CHILD'),
21 SET_KEYS: Symbol('SET_KEYS'),
18 SET_KEYS: Symbol('SET_KEYS'),
22 TRANSFER: Symbol('TRANSFER'),
19 TRANSFER: Symbol('TRANSFER'),
23− CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER')
20+ CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
24−}
21+};
25−
22+
26−const renderTxnPurpose = (purpose) =>
23+const renderTxnPurpose = purpose =>
27− purpose === TXN_PURPOSE.SET_MANAGEMENT_PROXY
24+ purpose === TXN_PURPOSE.SET_MANAGEMENT_PROXY
28− ? 'set this management proxy'
25+ ? 'set this management proxy'
29− : purpose === TXN_PURPOSE.SET_SPAWN_PROXY
26+ : purpose === TXN_PURPOSE.SET_SPAWN_PROXY
30− ? 'set this spawn proxy'
27+ ? 'set this spawn proxy'
31− : purpose === TXN_PURPOSE.SET_TRANSFER_PROXY
28+ : purpose === TXN_PURPOSE.SET_TRANSFER_PROXY
32− ? 'set this transfer proxy'
29+ ? 'set this transfer proxy'
33− : purpose === TXN_PURPOSE.CREATE_GALAXY
30+ : purpose === TXN_PURPOSE.CREATE_GALAXY
34− ? 'create this galaxy'
31+ ? 'create this galaxy'
35− : purpose === TXN_PURPOSE.ISSUE_CHILD
32+ : purpose === TXN_PURPOSE.ISSUE_CHILD
36− ? 'issue this point'
33+ ? 'issue this point'
37− : purpose === TXN_PURPOSE.SET_KEYS
34+ : purpose === TXN_PURPOSE.SET_KEYS
38− ? 'set these network keys'
35+ ? 'set these network keys'
39− : purpose === TXN_PURPOSE.TRANSFER
36+ : purpose === TXN_PURPOSE.TRANSFER
40− ? 'transfer this point'
37+ ? 'transfer this point'
41− : purpose === TXN_PURPOSE.CANCEL_TRANSFER
38+ : purpose === TXN_PURPOSE.CANCEL_TRANSFER
42− ? 'cancel this transfer'
39+ ? 'cancel this transfer'
43− : 'perform this transaction'
40+ : 'perform this transaction';
44
41
45const signTransaction = async config => {
42const signTransaction = async config => {
46−
47 let {
43 let {
48 wallet,
44 wallet,
49 walletType,
45 walletType,
@@ -54,15 +50,15 @@ const signTransaction = async config => {
54 nonce,
50 nonce,
55 chainId,
51 chainId,
56 gasPrice,
52 gasPrice,
57− gasLimit
53+ gasLimit,
58− } = config
54+ } = config;
59
55
60− nonce = toHex(nonce)
56+ nonce = toHex(nonce);
61− chainId = toHex(chainId)
57+ chainId = toHex(chainId);
62− gasPrice = toHex(toWei(gasPrice, 'gwei'))
58+ gasPrice = toHex(toWei(gasPrice, 'gwei'));
63− gasLimit = toHex(gasLimit)
59+ gasLimit = toHex(gasLimit);
64
60
65− const txParams = { nonce, chainId, gasPrice, gasLimit }
61+ const txParams = { nonce, chainId, gasPrice, gasLimit };
66
62
67 // NB (jtobin)
63 // NB (jtobin)
68 //
64 //
@@ -90,102 +86,104 @@ const signTransaction = async config => {
90 const eip155Params = {
86 const eip155Params = {
91 r: '0x00',
87 r: '0x00',
92 s: '0x00',
88 s: '0x00',
93− v: chainId
89+ v: chainId,
94− }
90+ };
95
91
96 const defaultEip155Networks = [
92 const defaultEip155Networks = [
97 NETWORK_NAMES.MAINNET,
93 NETWORK_NAMES.MAINNET,
98 NETWORK_NAMES.ROPSTEN,
94 NETWORK_NAMES.ROPSTEN,
99− NETWORK_NAMES.OFFLINE
95+ NETWORK_NAMES.OFFLINE,
100− ]
96+ ];
101
97
102 const needEip155Params =
98 const needEip155Params =
103 walletType === WALLET_NAMES.LEDGER &&
99 walletType === WALLET_NAMES.LEDGER &&
104− defaultEip155Networks.includes(networkType)
100+ defaultEip155Networks.includes(networkType);
105
101
106− const signingParams =
102+ const signingParams = needEip155Params
107− needEip155Params
108 ? Object.assign(txParams, eip155Params)
103 ? Object.assign(txParams, eip155Params)
109− : txParams
104+ : txParams;
110
105
111 const wal = wallet.matchWith({
106 const wal = wallet.matchWith({
112− Just: (w) => w.value,
107+ Just: w => w.value,
108+ Nothing: () => {
113− Nothing: () => { throw BRIDGE_ERROR.MISSING_WALLET }
109+ throw BRIDGE_ERROR.MISSING_WALLET;
110+ },
114− })
111+ });
115
112
116− const sec = wal.privateKey
113+ const sec = wal.privateKey;
117
114
118 const utx = txn.matchWith({
115 const utx = txn.matchWith({
119− Just: (tx) =>
120− Object.assign(tx.value, signingParams),
116+ Just: tx => Object.assign(tx.value, signingParams),
121 Nothing: () => {
117 Nothing: () => {
122− throw BRIDGE_ERROR.MISSING_TXN
118+ throw BRIDGE_ERROR.MISSING_TXN;
123− }
119+ },
124− })
120+ });
125
121
126− const stx = new Tx(utx)
122+ const stx = new Tx(utx);
127
123
128 if (walletType === WALLET_NAMES.LEDGER) {
124 if (walletType === WALLET_NAMES.LEDGER) {
129− await ledgerSignTransaction(stx, walletHdPath)
125+ await ledgerSignTransaction(stx, walletHdPath);
130 } else if (walletType === WALLET_NAMES.TREZOR) {
126 } else if (walletType === WALLET_NAMES.TREZOR) {
131− await trezorSignTransaction(stx, walletHdPath)
127+ await trezorSignTransaction(stx, walletHdPath);
132 } else {
128 } else {
133− stx.sign(sec)
129+ stx.sign(sec);
134 }
130 }
135
131
136 setStx(Just(stx));
132 setStx(Just(stx));
137 return stx;
133 return stx;
138−}
134+};
139
135
140const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
136const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
141 const txn = stx.matchWith({
137 const txn = stx.matchWith({
142− Just: (tx) => tx.value,
138+ Just: tx => tx.value,
143 Nothing: () => {
139 Nothing: () => {
144− throw BRIDGE_ERROR.MISSING_TXN
140+ throw BRIDGE_ERROR.MISSING_TXN;
145− }
141+ },
146 });
142 });
147
143
148 const rawTx = addHexPrefix(txn.serialize().toString('hex'));
144 const rawTx = addHexPrefix(txn.serialize().toString('hex'));
149
145
150 return new Promise(async (resolve, reject) => {
146 return new Promise(async (resolve, reject) => {
147+ web3.eth
151− web3.eth.sendSignedTransaction(rawTx)
148+ .sendSignedTransaction(rawTx)
152− .on('transactionHash', hash => {
149+ .on('transactionHash', hash => {
153− resolve(hash);
150+ resolve(hash);
154− })
151+ })
155− //TODO do we also reach this if network is slow? web3 only tries a set
152+ //TODO do we also reach this if network is slow? web3 only tries a set
156− // amount of times... should we instead do waitForTransactionConfirm
153+ // amount of times... should we instead do waitForTransactionConfirm
157− // in on-transactionHash? we don't care (much) about additional
154+ // in on-transactionHash? we don't care (much) about additional
158− // confirms anyway.
155+ // confirms anyway.
159− .on('confirmation', (confirmationNum, txn) => {
156+ .on('confirmation', (confirmationNum, txn) => {
160− confirmationCb(txn.transactionHash, confirmationNum + 1);
157+ confirmationCb(txn.transactionHash, confirmationNum + 1);
161− resolve(txn.transactionHash);
158+ resolve(txn.transactionHash);
162− })
159+ })
163− .on('error', err => {
160+ .on('error', err => {
164− // if there's a nonce error, but we used the gas tank, it's likely
161+ // if there's a nonce error, but we used the gas tank, it's likely
165− // that it's because the tank already submitted our transaction.
162+ // that it's because the tank already submitted our transaction.
166− // we just wait for first confirmation here.
163+ // we just wait for first confirmation here.
167− console.log('sent and got error', err);
164+ console.log('sent and got error', err);
165+ if (
168− if (doubtNonceError &&
166+ doubtNonceError &&
169− (err.message || '').includes("the tx doesn't have the correct nonce.")) {
167+ (err.message || '').includes("the tx doesn't have the correct nonce.")
168+ ) {
170− console.log('nonce error, likely from gas tank submission, ignoring');
169+ console.log('nonce error, likely from gas tank submission, ignoring');
171− const txHash = web3.utils.keccak256(rawTx);
170+ const txHash = web3.utils.keccak256(rawTx);
172− //TODO can we do does-exists check first?
171+ //TODO can we do does-exists check first?
173− //TODO max wait time before assume fail?
172+ //TODO max wait time before assume fail?
174− waitForTransactionConfirm(web3, txHash)
173+ waitForTransactionConfirm(web3, txHash).then(res => {
175− .then(res => {
176− if (res) {
174+ if (res) {
177− resolve(txHash);
175+ resolve(txHash);
178− confirmationCb(txHash, 1);
176+ confirmationCb(txHash, 1);
179− } else {
177+ } else {
180− reject('Unexpected tx failure');
178+ reject('Unexpected tx failure');
181− }
179+ }
182− });
180+ });
183− } else {
181+ } else {
184− reject(err.message);
182+ reject(err.message);
185− }
183+ }
186− });
184+ });
187 });
185 });
188−}
186+};
189
187
190// returns a Promise<bool>, where the bool indicates tx success/failure
188// returns a Promise<bool>, where the bool indicates tx success/failure
191const waitForTransactionConfirm = (web3, txHash) => {
189const waitForTransactionConfirm = (web3, txHash) => {
@@ -194,49 +192,49 @@ const waitForTransactionConfirm = (web3, txHash) => {
194 console.log('checking for confirm', txHash);
192 console.log('checking for confirm', txHash);
195 const receipt = await web3.eth.getTransactionReceipt(txHash);
193 const receipt = await web3.eth.getTransactionReceipt(txHash);
196 console.log('tried, got', receipt);
194 console.log('tried, got', receipt);
197− let confirmed = (receipt !== null);
195+ let confirmed = receipt !== null;
198 if (confirmed) resolve(receipt.status === true);
196 if (confirmed) resolve(receipt.status === true);
199 else setTimeout(checkForConfirm, 13000);
197 else setTimeout(checkForConfirm, 13000);
200− }
198+ };
201 checkForConfirm();
199 checkForConfirm();
202 });
200 });
203−}
201+};
204
202
205const isTransactionConfirmed = async (web3, txHash) => {
203const isTransactionConfirmed = async (web3, txHash) => {
206 const receipt = await web3.eth.getTransactionReceipt(txHash);
204 const receipt = await web3.eth.getTransactionReceipt(txHash);
207 console.log('got confirm state', receipt !== null, receipt.confirmations);
205 console.log('got confirm state', receipt !== null, receipt.confirmations);
208− return (receipt !== null);
206+ return receipt !== null;
209−}
207+};
210
208
211−const hexify = val => addHexPrefix(val.toString('hex'))
209+const hexify = val => addHexPrefix(val.toString('hex'));
212
210
213const renderSignedTx = stx => ({
211const renderSignedTx = stx => ({
214 messageHash: hexify(stx.hash()),
212 messageHash: hexify(stx.hash()),
215 v: hexify(stx.v),
213 v: hexify(stx.v),
216 s: hexify(stx.s),
214 s: hexify(stx.s),
217 r: hexify(stx.r),
215 r: hexify(stx.r),
218− rawTransaction: hexify(stx.serialize())
216+ rawTransaction: hexify(stx.serialize()),
219−})
217+});
220
218
221const getTxnInfo = async (web3, addr) => {
219const getTxnInfo = async (web3, addr) => {
222− let nonce = await web3.eth.getTransactionCount(addr)
220+ let nonce = await web3.eth.getTransactionCount(addr);
223− let chainId = await web3.eth.net.getId()
221+ let chainId = await web3.eth.net.getId();
224− let gasPrice = await web3.eth.getGasPrice()
222+ let gasPrice = await web3.eth.getGasPrice();
225 return {
223 return {
226 nonce: nonce,
224 nonce: nonce,
227 chainId: chainId,
225 chainId: chainId,
228− gasPrice: fromWei(gasPrice, 'gwei')
226+ gasPrice: fromWei(gasPrice, 'gwei'),
229− }
227+ };
230−}
228+};
231
229
232const canDecodePatp = p => {
230const canDecodePatp = p => {
233 try {
231 try {
234− ob.patp2dec(p)
232+ ob.patp2dec(p);
235− return true
233+ return true;
236 } catch (_) {
234 } catch (_) {
237− return false
235+ return false;
238 }
236 }
239−}
237+};
240
238
241export {
239export {
242 signTransaction,
240 signTransaction,
@@ -252,4 +250,4 @@ export {
252 toWei,
250 toWei,
253 fromWei,
251 fromWei,
254 canDecodePatp,
252 canDecodePatp,
255−}
253+};
src/bridge/lib/validators.js+100−91
@@ -1,9 +1,9 @@
1import * as bip39 from 'bip39';
1import * as bip39 from 'bip39';
2import * as ob from 'urbit-ob';
2import * as ob from 'urbit-ob';
3
3
4−import { isValidAddress } from './wallet'
4+import { isValidAddress } from './wallet';
5
5
6−const hexRegExp = /[0-9A-Fa-f]{64}/g
6+const hexRegExp = /[0-9A-Fa-f]{64}/g;
7
7
8// // Note that this validator also transforms data
8// // Note that this validator also transforms data
9// const validateBase64 = ({ data, pass, error }) => {
9// const validateBase64 = ({ data, pass, error }) => {
@@ -30,109 +30,118 @@ const hexRegExp = /[0-9A-Fa-f]{64}/g
30// };
30// };
31
31
32// Validates a bip39 mnemonic
32// Validates a bip39 mnemonic
33−const validateMnemonic = m => simpleValidatorWrapper({
33+const validateMnemonic = m =>
34+ simpleValidatorWrapper({
34− prevMessage: m,
35+ prevMessage: m,
35− validator: d => bip39.validateMnemonic(d),
36+ validator: d => bip39.validateMnemonic(d),
36− errorMessage: 'This is not a valid mnemonic.'
37+ errorMessage: 'This is not a valid mnemonic.',
37−});
38+ });
38
39
39// Checks an empty field
40// Checks an empty field
40−const validateNotEmpty = m => simpleValidatorWrapper({
41+const validateNotEmpty = m =>
42+ simpleValidatorWrapper({
41− prevMessage: m,
43+ prevMessage: m,
42− validator: d => d.length > 1,
44+ validator: d => d.length > 1,
43− errorMessage: 'This field is required.'
45+ errorMessage: 'This field is required.',
44−});
46+ });
45
47
46// Checks if a patp is a valid galaxy
48// Checks if a patp is a valid galaxy
49+const validateGalaxy = m =>
47−const validateGalaxy = m => simpleValidatorWrapper({
50+ simpleValidatorWrapper({
48− prevMessage: m,
51+ prevMessage: m,
49− validator: d => {
52+ validator: d => {
50− let point
53+ let point;
51− try {
54+ try {
52− point = parseInt(ob.patp2dec(d), 10)
55+ point = parseInt(ob.patp2dec(d), 10);
53− return point >= 0 && point < 256
56+ return point >= 0 && point < 256;
54− } catch(e) {
57+ } catch (e) {
55− return false
58+ return false;
56− }
59+ }
57− },
60+ },
58− errorMessage: 'This is not a valid galaxy'
61+ errorMessage: 'This is not a valid galaxy',
59−});
62+ });
60−
63+
64+const validatePoint = m =>
61−const validatePoint = m => simpleValidatorWrapper({
65+ simpleValidatorWrapper({
62− prevMessage: m,
66+ prevMessage: m,
63− validator: d => {
67+ validator: d => {
64− try {
68+ try {
65− return ob.isValidPatp(d)
69+ return ob.isValidPatp(d);
66− } catch(e) {
70+ } catch (e) {
67− return false
71+ return false;
68− }
72+ }
69− },
73+ },
70− errorMessage: 'This is not a valid point'
74+ errorMessage: 'This is not a valid point',
71−});
75+ });
72−
76+
77+const validateTicket = m =>
73−const validateTicket = m => simpleValidatorWrapper({
78+ simpleValidatorWrapper({
74− prevMessage: m,
79+ prevMessage: m,
75− validator: d => {
80+ validator: d => {
76− try {
81+ try {
77− return ob.isValidPatq(d)
82+ return ob.isValidPatq(d);
78− } catch(e) {
83+ } catch (e) {
79− return false
84+ return false;
80− }
85+ }
81− },
86+ },
82− errorMessage: 'This is not a valid ticket'
87+ errorMessage: 'This is not a valid ticket',
83−});
88+ });
84−
89+
90+const validateShard = m =>
85−const validateShard = m => simpleValidatorWrapper({
91+ simpleValidatorWrapper({
86− prevMessage: m,
92+ prevMessage: m,
87− validator: d => {
93+ validator: d => {
88− try {
94+ try {
89− return ob.isValidPatq(d)
95+ return ob.isValidPatq(d);
90− } catch(e) {
96+ } catch (e) {
91− return false
97+ return false;
92− }
98+ }
93− },
99+ },
94− errorMessage: 'This is not a valid shard'
100+ errorMessage: 'This is not a valid shard',
95−});
101+ });
96−
102+
103+const validateLength = (m, l) =>
97−const validateLength = (m, l) => simpleValidatorWrapper({
104+ simpleValidatorWrapper({
98− prevMessage: m,
105+ prevMessage: m,
99− validator: d => d.length === l,
106+ validator: d => d.length === l,
100− errorMessage: 'This is of an invalid length'
107+ errorMessage: 'This is of an invalid length',
101−});
108+ });
102−
109+
103−const validateNetworkKey = m => simpleValidatorWrapper({
110+const validateNetworkKey = m =>
111+ simpleValidatorWrapper({
104− prevMessage: m,
112+ prevMessage: m,
105− validator: d => hexRegExp.test(d),
113+ validator: d => hexRegExp.test(d),
106− errorMessage: 'This is not a valid network key'
114+ errorMessage: 'This is not a valid network key',
107−});
115+ });
108−
116+
109−const validateNetworkSeed = m => simpleValidatorWrapper({
117+const validateNetworkSeed = m =>
118+ simpleValidatorWrapper({
110− prevMessage: m,
119+ prevMessage: m,
111− validator: d => hexRegExp.test(d),
120+ validator: d => hexRegExp.test(d),
112− errorMessage: 'This is not a valid network seed'
121+ errorMessage: 'This is not a valid network seed',
113−});
122+ });
114
123
115// Checks if a string is a valid ethereum address
124// Checks if a string is a valid ethereum address
116−const validateEthereumAddress = m => simpleValidatorWrapper({
125+const validateEthereumAddress = m =>
126+ simpleValidatorWrapper({
117− prevMessage: m,
127+ prevMessage: m,
118− validator: d => isValidAddress(d),
128+ validator: d => isValidAddress(d),
119− errorMessage: 'This is not a valid Ethereum address'
129+ errorMessage: 'This is not a valid Ethereum address',
120−});
130+ });
121
131
122// Wraps single validation functions in a controlled and predictable way.
132// Wraps single validation functions in a controlled and predictable way.
123const simpleValidatorWrapper = config => {
133const simpleValidatorWrapper = config => {
124−
125 // If a previous validation has already failed, skip this validation and
134 // If a previous validation has already failed, skip this validation and
126 // return the prev message to the next stage in the validation function chain.
135 // return the prev message to the next stage in the validation function chain.
127 // Failed validations should drop all the way down the chain and drop out of
136 // Failed validations should drop all the way down the chain and drop out of
128 // the output.
137 // the output.
129− if (config.prevMessage.pass !== true) return config.prevMessage
138+ if (config.prevMessage.pass !== true) return config.prevMessage;
130
139
131 // Run the validator and return the result.
140 // Run the validator and return the result.
132 return config.validator(config.prevMessage.data)
141 return config.validator(config.prevMessage.data)
133 ? newMessage(config.prevMessage.data, true, '')
142 ? newMessage(config.prevMessage.data, true, '')
134− : newMessage(config.prevMessage.data, false, config.errorMessage)
143+ : newMessage(config.prevMessage.data, false, config.errorMessage);
135−}
144+};
136
145
137// Validation message
146// Validation message
138// {
147// {
@@ -148,7 +157,7 @@ const newMessage = (data, pass, error) => ({
148 pass,
157 pass,
149 // If data has failed a validator, the error message goes here.
158 // If data has failed a validator, the error message goes here.
150 error,
159 error,
151−})
160+});
152
161
153export {
162export {
154 // validateBase64,
163 // validateBase64,
@@ -163,5 +172,5 @@ export {
163 validateShard,
172 validateShard,
164 validateLength,
173 validateLength,
165 validateNetworkKey,
174 validateNetworkKey,
166− validateNetworkSeed
175+ validateNetworkSeed,
167−}
176+};
src/bridge/lib/wallet.js+82−84
@@ -1,17 +1,18 @@
1−import * as bip32 from 'bip32'
1+import * as bip32 from 'bip32';
2−import * as bip39 from 'bip39'
2+import * as bip39 from 'bip39';
3−import keccak from 'keccak'
3+import keccak from 'keccak';
4−import * as lodash from 'lodash'
4+import * as lodash from 'lodash';
5−import Maybe from 'folktale/maybe'
5+import Maybe from 'folktale/maybe';
6−import * as secp256k1 from 'secp256k1'
6+import * as secp256k1 from 'secp256k1';
7−import * as ob from 'urbit-ob'
7+import * as ob from 'urbit-ob';
8−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
8+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
9−import { isAddress } from 'web3-utils'
9+import { isAddress } from 'web3-utils';
10−
10+
11−const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0"
11+const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
12−
12+
13−const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000'
13+const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
14+const CURVE_ZERO_ADDR =
14−const CURVE_ZERO_ADDR = "0x0000000000000000000000000000000000000000000000000000000000000000"
15+ '0x0000000000000000000000000000000000000000000000000000000000000000';
15
16
16const WALLET_NAMES = {
17const WALLET_NAMES = {
17 MNEMONIC: Symbol('MNEMONIC'),
18 MNEMONIC: Symbol('MNEMONIC'),
@@ -20,120 +21,117 @@ const WALLET_NAMES = {
20 LEDGER: Symbol('LEDGER'),
21 LEDGER: Symbol('LEDGER'),
21 TREZOR: Symbol('TREZOR'),
22 TREZOR: Symbol('TREZOR'),
22 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
23 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
23− KEYSTORE: Symbol('KEYSTORE')
24+ KEYSTORE: Symbol('KEYSTORE'),
24−}
25+};
25
26
26function EthereumWallet(privateKey) {
27function EthereumWallet(privateKey) {
27− this.privateKey = privateKey
28+ this.privateKey = privateKey;
28− this.publicKey = secp256k1.publicKeyCreate(this.privateKey)
29+ this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
29− const pub = this.publicKey.toString('hex')
30+ const pub = this.publicKey.toString('hex');
30− this.address = addressFromSecp256k1Public(pub)
31+ this.address = addressFromSecp256k1Public(pub);
31}
32}
32
33
33−const renderWalletType = (wallet) =>
34+const renderWalletType = wallet =>
34− wallet === WALLET_NAMES.MNEMONIC
35+ wallet === WALLET_NAMES.MNEMONIC
35− ? 'Mnemonic'
36+ ? 'Mnemonic'
36− : wallet === WALLET_NAMES.TICKET
37+ : wallet === WALLET_NAMES.TICKET
37− ? 'Ticket'
38+ ? 'Ticket'
38− : wallet === WALLET_NAMES.SHARDS
39+ : wallet === WALLET_NAMES.SHARDS
39− ? 'Ticket Shards'
40+ ? 'Ticket Shards'
40− : wallet === WALLET_NAMES.LEDGER
41+ : wallet === WALLET_NAMES.LEDGER
41− ? 'Ledger'
42+ ? 'Ledger'
42− : wallet === WALLET_NAMES.TREZOR
43+ : wallet === WALLET_NAMES.TREZOR
43− ? 'Trezor'
44+ ? 'Trezor'
44− : wallet === WALLET_NAMES.PRIVATE_KEY
45+ : wallet === WALLET_NAMES.PRIVATE_KEY
45− ? 'Private Key'
46+ ? 'Private Key'
46− : wallet === WALLET_NAMES.KEYSTORE
47+ : wallet === WALLET_NAMES.KEYSTORE
47− ? 'Keystore File'
48+ ? 'Keystore File'
48− : 'Wallet'
49+ : 'Wallet';
49
50
50const addressFromSecp256k1Public = pub => {
51const addressFromSecp256k1Public = pub => {
51− const compressed = false
52+ const compressed = false;
52 const uncompressed = secp256k1.publicKeyConvert(
53 const uncompressed = secp256k1.publicKeyConvert(
53 Buffer.from(pub, 'hex'),
54 Buffer.from(pub, 'hex'),
54 compressed
55 compressed
55− )
56+ );
56− const chopped = uncompressed.slice(1) // chop parity byte
57+ const chopped = uncompressed.slice(1); // chop parity byte
57− const hashed = keccak256(chopped)
58+ const hashed = keccak256(chopped);
58− const addr = addHexPrefix(hashed.slice(-20).toString('hex'))
59+ const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
59− return toChecksumAddress(addr)
60+ return toChecksumAddress(addr);
60−}
61+};
61
62
62−const addHexPrefix = hex =>
63+const addHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex : '0x' + hex);
63− hex.slice(0, 2) === '0x'
64− ? hex
65− : '0x' + hex
66
64
67−const stripHexPrefix = hex =>
65+const stripHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex.slice(2) : hex);
68− hex.slice(0, 2) === '0x'
69− ? hex.slice(2)
70− : hex
71
66
72const keccak256 = str =>
67const keccak256 = str =>
73− keccak('keccak256').update(str).digest()
68+ keccak('keccak256')
69+ .update(str)
70+ .digest();
74
71
75−const isValidAddress = a => (('0x0' === a) || isAddress(a))
72+const isValidAddress = a => '0x0' === a || isAddress(a);
76
73
77−const toChecksumAddress = (address) => {
74+const toChecksumAddress = address => {
78− const addr = stripHexPrefix(address).toLowerCase()
75+ const addr = stripHexPrefix(address).toLowerCase();
79− const hash = keccak256(addr).toString('hex')
76+ const hash = keccak256(addr).toString('hex');
80
77
81− return lodash.reduce(addr, (acc, char, idx) =>
78+ return lodash.reduce(
79+ addr,
80+ (acc, char, idx) =>
82− parseInt(hash[idx], 16) >= 8
81+ parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
83− ? acc + char.toUpperCase()
84− : acc + char,
85− '0x')
82+ '0x'
83+ );
86−}
84+};
87
85
88const eqAddr = (addr0, addr1) =>
86const eqAddr = (addr0, addr1) =>
89− toChecksumAddress(addr0) === toChecksumAddress(addr1)
87+ toChecksumAddress(addr0) === toChecksumAddress(addr1);
90
88
91const urbitWalletFromTicket = async (ticket, point, passphrase) => {
89const urbitWalletFromTicket = async (ticket, point, passphrase) => {
92− if (typeof point === 'string') { //TODO why not in kg?
90+ if (typeof point === 'string') {
91+ //TODO why not in kg?
93 point = ob.patp2dec(point);
92 point = ob.patp2dec(point);
94 }
93 }
95 let uhdw = await kg.generateWallet({
94 let uhdw = await kg.generateWallet({
96 ticket: ticket,
95 ticket: ticket,
97 ship: point,
96 ship: point,
98− passphrase: passphrase
97+ passphrase: passphrase,
99 });
98 });
100 return uhdw;
99 return uhdw;
101−}
100+};
102−
103
101
104const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
102const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
105− if (typeof point === 'string') { //TODO why not in kg?
103+ if (typeof point === 'string') {
104+ //TODO why not in kg?
106 point = ob.patp2dec(point);
105 point = ob.patp2dec(point);
107 }
106 }
108 return await kg.generateOwnershipWallet({
107 return await kg.generateOwnershipWallet({
109 ticket,
108 ticket,
110 ship: point,
109 ship: point,
111− passphrase
110+ passphrase,
112− })
111+ });
113−}
112+};
114
113
115const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
114const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
116− const seed =
117− bip39.validateMnemonic(mnemonic)
115+ const seed = bip39.validateMnemonic(mnemonic)
118 ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
116 ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
119− : Maybe.Nothing()
117+ : Maybe.Nothing();
120
118
121 const toWallet = (sd, path) => {
119 const toWallet = (sd, path) => {
122− let wal
120+ let wal;
123 try {
121 try {
124− const hd = bip32.fromSeed(sd)
122+ const hd = bip32.fromSeed(sd);
125 wal = hd.derivePath(path);
123 wal = hd.derivePath(path);
126 wal.address = addressFromSecp256k1Public(wal.publicKey);
124 wal.address = addressFromSecp256k1Public(wal.publicKey);
127 wal = Maybe.Just(wal);
125 wal = Maybe.Just(wal);
128 } catch (_) {
126 } catch (_) {
129− wal = Maybe.Nothing()
127+ wal = Maybe.Nothing();
130 }
128 }
131− return wal
129+ return wal;
132− }
130+ };
133
131
134− const wallet = seed.chain(sd => toWallet(sd, hdpath))
132+ const wallet = seed.chain(sd => toWallet(sd, hdpath));
135− return wallet
133+ return wallet;
136−}
134+};
137
135
138export {
136export {
139 DEFAULT_HD_PATH,
137 DEFAULT_HD_PATH,
@@ -149,5 +147,5 @@ export {
149 ownershipWalletFromTicket,
147 ownershipWalletFromTicket,
150 walletFromMnemonic,
148 walletFromMnemonic,
151 addHexPrefix,
149 addHexPrefix,
152− EthereumWallet
150+ EthereumWallet,
153−}
151+};
src/bridge/views/AcceptTransfer.js+59−50
@@ -1,18 +1,23 @@
1−import { Just } from 'folktale/maybe'
1+import { Just } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Row, Col, H1, P } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base';
4+import {
5+ InnerLabel,
6+ AddressInput,
7+ ShowBlockie,
8+ Anchor,
4−import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
9+} from '../components/Base';
5−import * as azimuth from 'azimuth-js'
10+import * as azimuth from 'azimuth-js';
6−import * as ob from 'urbit-ob'
11+import * as ob from 'urbit-ob';
7−import * as need from '../lib/need'
12+import * as need from '../lib/need';
8−
13+
9−import StatelessTransaction from '../components/StatelessTransaction'
14+import StatelessTransaction from '../components/StatelessTransaction';
10−import { NETWORK_NAMES } from '../lib/network'
15+import { NETWORK_NAMES } from '../lib/network';
11−import { isValidAddress } from '../lib/wallet'
16+import { isValidAddress } from '../lib/wallet';
12
17
13class AcceptTransfer extends React.Component {
18class AcceptTransfer extends React.Component {
14 constructor(props) {
19 constructor(props) {
15− super(props)
20+ super(props);
16
21
17 const receivingAddress = need.address(props);
22 const receivingAddress = need.address(props);
18
23
@@ -21,91 +26,95 @@ class AcceptTransfer extends React.Component {
21 this.state = {
26 this.state = {
22 receivingAddress: receivingAddress,
27 receivingAddress: receivingAddress,
23 incomingPoint: incomingPoint,
28 incomingPoint: incomingPoint,
24− }
29+ };
25
30
26− this.handleAddressInput = this.handleAddressInput.bind(this)
31+ this.handleAddressInput = this.handleAddressInput.bind(this);
27− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
32+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
28 this.statelessRef = React.createRef();
33 this.statelessRef = React.createRef();
29 }
34 }
30
35
31 handleAddressInput(proxyAddress) {
36 handleAddressInput(proxyAddress) {
32− this.setState({ proxyAddress })
37+ this.setState({ proxyAddress });
33− this.statelessRef.current.clearTxn()
38+ this.statelessRef.current.clearTxn();
34 }
39 }
35
40
36 createUnsignedTxn() {
41 createUnsignedTxn() {
37− const { state, props } = this
42+ const { state, props } = this;
38
43
39 const validContracts = need.contracts(props);
44 const validContracts = need.contracts(props);
40
45
41 const validPoint = need.pointCursor(props); //TODO state.incomingPoint ?
46 const validPoint = need.pointCursor(props); //TODO state.incomingPoint ?
42
47
48+ return Just(
43− return Just(azimuth.ecliptic.transferPoint(
49+ azimuth.ecliptic.transferPoint(
44− validContracts,
50+ validContracts,
45− validPoint,
51+ validPoint,
46− state.receivingAddress,
52+ state.receivingAddress,
47− true
53+ true
48− ))
54+ )
55+ );
49 }
56 }
50
57
51 render() {
58 render() {
52− const { state, props } = this
59+ const { state, props } = this;
53− const validAddress = isValidAddress(state.receivingAddress)
60+ const validAddress = isValidAddress(state.receivingAddress);
54− const canGenerate = validAddress === true
61+ const canGenerate = validAddress === true;
55
62
56 const esvisible =
63 const esvisible =
57− props.networkType === NETWORK_NAMES.ROPSTEN ||
64+ props.networkType === NETWORK_NAMES.ROPSTEN ||
58− props.networkType === NETWORK_NAMES.MAINNET
65+ props.networkType === NETWORK_NAMES.MAINNET;
59
66
60 const esdomain =
67 const esdomain =
61− props.networkType === NETWORK_NAMES.ROPSTEN
68+ props.networkType === NETWORK_NAMES.ROPSTEN
62− ? "ropsten.etherscan.io"
69+ ? 'ropsten.etherscan.io'
63− : "etherscan.io"
70+ : 'etherscan.io';
64
71
65 return (
72 return (
66 <Row>
73 <Row>
67 <Col>
74 <Col>
68 <H1>
75 <H1>
76+ {'Accept Transfer of '}{' '}
69− { 'Accept Transfer of '} <code>{ ` ${ob.patp(state.incomingPoint)} ` }</code>
77+ <code>{` ${ob.patp(state.incomingPoint)} `}</code>
70 </H1>
78 </H1>
71
79
72 <P>
80 <P>
73− {
74− "By default, the recipient is the address you're logged in " +
81+ {"By default, the recipient is the address you're logged in " +
75− "as. But you can transfer to any address you like."
82+ 'as. But you can transfer to any address you like.'}
76− }
77 </P>
83 </P>
78
84
79 <AddressInput
85 <AddressInput
80− className='mono mt-8'
86+ className="mono mt-8"
81− prop-size='lg'
87+ prop-size="lg"
82− prop-format='innerLabel'
88+ prop-format="innerLabel"
83− value={ state.receivingAddress }
89+ value={state.receivingAddress}
84− onChange={ v => this.handleAddressInput(v) }>
90+ onChange={v => this.handleAddressInput(v)}>
85− <InnerLabel>{ 'Receiving Address' }</InnerLabel>
91+ <InnerLabel>{'Receiving Address'}</InnerLabel>
86 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
92 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
87 </AddressInput>
93 </AddressInput>
88
94
89 <Anchor
95 <Anchor
90 className={'mt-1 sm'}
96 className={'mt-1 sm'}
91− prop-size='sm'
97+ prop-size="sm"
98+ prop-disabled={
92− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
99+ !isValidAddress(state.receivingAddress) || !esvisible
100+ }
93 target={'_blank'}
101 target={'_blank'}
94 href={`https://${esdomain}/address/${state.receivingAddress}`}>
102 href={`https://${esdomain}/address/${state.receivingAddress}`}>
95− {'View on Etherscan ↗'}
103+ {'View on Etherscan ↗'}
96 </Anchor>
104 </Anchor>
97
105
98 <StatelessTransaction
106 <StatelessTransaction
99 // Upper scope
107 // Upper scope
100 {...props}
108 {...props}
101 // Other
109 // Other
102− canGenerate={ canGenerate }
110+ canGenerate={canGenerate}
103 createUnsignedTxn={this.createUnsignedTxn}
111 createUnsignedTxn={this.createUnsignedTxn}
104− ref={ this.statelessRef } />
112+ ref={this.statelessRef}
113+ />
105 </Col>
114 </Col>
106 </Row>
115 </Row>
107− )
116+ );
108 }
117 }
109}
118}
110
119
111−export default AcceptTransfer
120+export default AcceptTransfer;
src/bridge/views/CancelTransfer.js+35−37
@@ -1,29 +1,29 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Row, Col, H1, P } from '../components/Base'
3+import { Row, Col, H1, P } from '../components/Base';
4−import * as azimuth from 'azimuth-js'
4+import * as azimuth from 'azimuth-js';
5−import * as ob from 'urbit-ob'
5+import * as ob from 'urbit-ob';
6−import * as need from '../lib/need'
6+import * as need from '../lib/need';
7
7
8−import StatelessTransaction from '../components/StatelessTransaction'
8+import StatelessTransaction from '../components/StatelessTransaction';
9−import { ETH_ZERO_ADDR } from '../lib/wallet'
9+import { ETH_ZERO_ADDR } from '../lib/wallet';
10
10
11class CancelTransfer extends React.Component {
11class CancelTransfer extends React.Component {
12 constructor(props) {
12 constructor(props) {
13− super(props)
13+ super(props);
14
14
15 const pointInTransfer = need.pointCursor(props);
15 const pointInTransfer = need.pointCursor(props);
16
16
17 this.state = {
17 this.state = {
18 proxyAddress: '',
18 proxyAddress: '',
19 pointInTransfer: pointInTransfer,
19 pointInTransfer: pointInTransfer,
20− }
20+ };
21
21
22− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
22+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
23 }
23 }
24
24
25 createUnsignedTxn() {
25 createUnsignedTxn() {
26− const { props } = this
26+ const { props } = this;
27
27
28 const validContracts = need.contracts(props);
28 const validContracts = need.contracts(props);
29
29
@@ -33,46 +33,44 @@ class CancelTransfer extends React.Component {
33 validContracts,
33 validContracts,
34 validPoint,
34 validPoint,
35 ETH_ZERO_ADDR
35 ETH_ZERO_ADDR
36− )
36+ );
37
37
38− return Maybe.Just(txn)
38+ return Maybe.Just(txn);
39 }
39 }
40
40
41 render() {
41 render() {
42− const { props, state } = this
42+ const { props, state } = this;
43
43
44− const online = Maybe.Just.hasInstance(props.web3)
44+ const online = Maybe.Just.hasInstance(props.web3);
45
45
46 const proxy = online
46 const proxy = online
47 ? props.pointCache[state.pointInTransfer].transferProxy
47 ? props.pointCache[state.pointInTransfer].transferProxy
48− : 'any outgoing addresses'
48+ : 'any outgoing addresses';
49
49
50 // const canGenerate = validAddress === true
50 // const canGenerate = validAddress === true
51
51
52− const canGenerate = true
52+ const canGenerate = true;
53
53
54 return (
54 return (
55− <Row>
55+ <Row>
56− <Col>
56+ <Col>
57− <H1>
57+ <H1>
58+ {'Cancel Transfer of '}{' '}
58− { 'Cancel Transfer of '} <code>{ ` ${ob.patp(state.pointInTransfer)} ` }</code>
59+ <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
59− </H1>
60+ </H1>
60−
61+
61− <P>
62− {
63− `This action will cancel the transfer to ${proxy}.`
62+ <P>{`This action will cancel the transfer to ${proxy}.`}</P>
64− }
65− </P>
66− <StatelessTransaction
63+ <StatelessTransaction
67− // Upper scope
64+ // Upper scope
68− {...props}
65+ {...props}
69− // Other
66+ // Other
70− canGenerate={ canGenerate }
67+ canGenerate={canGenerate}
71− createUnsignedTxn={this.createUnsignedTxn} />
68+ createUnsignedTxn={this.createUnsignedTxn}
69+ />
72 </Col>
70 </Col>
73 </Row>
71 </Row>
74− )
72+ );
75 }
73 }
76}
74}
77
75
78−export default CancelTransfer
76+export default CancelTransfer;
src/bridge/views/CreateGalaxy.js+96−106
@@ -1,45 +1,42 @@
1−import React from 'react'
1+import React from 'react';
2−import Maybe from 'folktale/maybe'
2+import Maybe from 'folktale/maybe';
3−import * as azimuth from 'azimuth-js'
3+import * as azimuth from 'azimuth-js';
4−import * as ob from 'urbit-ob'
4+import * as ob from 'urbit-ob';
5−import * as need from '../lib/need'
5+import * as need from '../lib/need';
6−
6+
7−import { Button, } from '../components/Base'
7+import { Button } from '../components/Base';
8−import { Row, Col, H1, P, Anchor } from '../components/Base'
8+import { Row, Col, H1, P, Anchor } from '../components/Base';
9−import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie } from '../components/Base'
10−import StatelessTransaction from '../components/StatelessTransaction'
11−
12−import { NETWORK_NAMES } from '../lib/network'
13−import { canDecodePatp } from '../lib/txn'
14−
15import {
9import {
16− ETH_ZERO_ADDR,
10+ InnerLabel,
17− isValidAddress,
11+ GalaxyInput,
18− eqAddr
12+ AddressInput,
19−} from '../lib/wallet'
13+ ValidatedSigil,
14+ ShowBlockie,
15+} from '../components/Base';
16+import StatelessTransaction from '../components/StatelessTransaction';
20
17
21−import {
18+import { NETWORK_NAMES } from '../lib/network';
22− isValidGalaxy
19+import { canDecodePatp } from '../lib/txn';
23−} from '../lib/lib'
20+
21+import { ETH_ZERO_ADDR, isValidAddress, eqAddr } from '../lib/wallet';
24
22
23+import { isValidGalaxy } from '../lib/lib';
25
24
26const buttonTriState = status => {
25const buttonTriState = status => {
27− if (status === null) return 'blue'
26+ if (status === null) return 'blue';
28− if (status === false) return 'yellow'
27+ if (status === false) return 'yellow';
29− if (status === true) return 'green'
28+ if (status === true) return 'green';
30−}
29+};
31
30
32const buttonTriStateText = status => {
31const buttonTriStateText = status => {
33− if (status === null) return 'Confirm Galaxy Availablility'
32+ if (status === null) return 'Confirm Galaxy Availablility';
34− if (status === false) return 'Galaxy is Not Available'
33+ if (status === false) return 'Galaxy is Not Available';
35− if (status === true) return 'Galaxy is Available'
34+ if (status === true) return 'Galaxy is Available';
36−}
35+};
37−
38−
39
36
40class CreateGalaxy extends React.Component {
37class CreateGalaxy extends React.Component {
41 constructor(props) {
38 constructor(props) {
42− super(props)
39+ super(props);
43
40
44 const galaxyOwner = need.address(props);
41 const galaxyOwner = need.address(props);
45
42
@@ -47,126 +44,122 @@ class CreateGalaxy extends React.Component {
47 galaxyOwner: galaxyOwner,
44 galaxyOwner: galaxyOwner,
48 galaxyName: '',
45 galaxyName: '',
49 isAvailable: null,
46 isAvailable: null,
50− }
47+ };
51
48
52− this.handleAddressInput = this.handleAddressInput.bind(this)
49+ this.handleAddressInput = this.handleAddressInput.bind(this);
53− this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this)
50+ this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this);
54− this.confirmAvailability = this.confirmAvailability.bind(this)
51+ this.confirmAvailability = this.confirmAvailability.bind(this);
55− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
52+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
56 this.statelessRef = React.createRef();
53 this.statelessRef = React.createRef();
57 }
54 }
58
55
59− handleGalaxyNameInput = (galaxyName) => {
56+ handleGalaxyNameInput = galaxyName => {
60− this.setState({ galaxyName, isAvailable: null })
57+ this.setState({ galaxyName, isAvailable: null });
61− this.statelessRef.current.clearTxn()
58+ this.statelessRef.current.clearTxn();
62− }
59+ };
63
60
64− handleAddressInput = (galaxyOwner) => {
61+ handleAddressInput = galaxyOwner => {
65− this.setState({ galaxyOwner })
62+ this.setState({ galaxyOwner });
66− this.statelessRef.current.clearTxn()
63+ this.statelessRef.current.clearTxn();
67− }
64+ };
68
65
69 createUnsignedTxn = () => {
66 createUnsignedTxn = () => {
70− const { state, props } = this
67+ const { state, props } = this;
71− if (isValidAddress(state.galaxyOwner) === false) return Maybe.Nothing()
68+ if (isValidAddress(state.galaxyOwner) === false) return Maybe.Nothing();
72− if (state.isAvailable === false) return Maybe.Nothing()
69+ if (state.isAvailable === false) return Maybe.Nothing();
73− if (canDecodePatp(state.galaxyName) === false) return Maybe.Nothing()
70+ if (canDecodePatp(state.galaxyName) === false) return Maybe.Nothing();
74− if (isValidGalaxy(state.galaxyName) === false) return Maybe.Nothing()
71+ if (isValidGalaxy(state.galaxyName) === false) return Maybe.Nothing();
75
72
76 const validContracts = need.contracts(props);
73 const validContracts = need.contracts(props);
77
74
78− const galaxyDec = parseInt(ob.patp2dec(state.galaxyName), 10)
75+ const galaxyDec = parseInt(ob.patp2dec(state.galaxyName), 10);
79
76
80 const txn = azimuth.ecliptic.createGalaxy(
77 const txn = azimuth.ecliptic.createGalaxy(
81 validContracts,
78 validContracts,
82 galaxyDec,
79 galaxyDec,
83 state.galaxyOwner
80 state.galaxyOwner
84− )
81+ );
85
82
86− return Maybe.Just(txn)
83+ return Maybe.Just(txn);
87− }
84+ };
88
85
89 confirmAvailability = async () => {
86 confirmAvailability = async () => {
90− const { state, props } = this
87+ const { state, props } = this;
91
88
92 if (canDecodePatp(state.galaxyName) === false) {
89 if (canDecodePatp(state.galaxyName) === false) {
93− this.setState({ isAvailable: false })
90+ this.setState({ isAvailable: false });
94− return
91+ return;
95 }
92 }
96
93
97 const validContracts = need.contracts(props);
94 const validContracts = need.contracts(props);
98
95
99− const galaxyDec = ob.patp2dec(state.galaxyName)
96+ const galaxyDec = ob.patp2dec(state.galaxyName);
100
97
101 const currentOwner = await azimuth.azimuth.getOwner(
98 const currentOwner = await azimuth.azimuth.getOwner(
102 validContracts,
99 validContracts,
103 galaxyDec
100 galaxyDec
104− )
101+ );
105
102
106− const available = eqAddr(currentOwner, ETH_ZERO_ADDR)
103+ const available = eqAddr(currentOwner, ETH_ZERO_ADDR);
107
104
108− this.setState({ isAvailable: available })
105+ this.setState({ isAvailable: available });
109− }
106+ };
110
107
111 render() {
108 render() {
112− const { props, state } = this
109+ const { props, state } = this;
113
110
114− const validAddress = isValidAddress(state.galaxyOwner)
111+ const validAddress = isValidAddress(state.galaxyOwner);
115− const validGalaxy = isValidGalaxy(state.galaxyName)
112+ const validGalaxy = isValidGalaxy(state.galaxyName);
116
113
117− const canGenerate = validAddress === true &&
114+ const canGenerate =
115+ validAddress === true &&
118− validGalaxy === true &&
116+ validGalaxy === true &&
119− state.isAvailable === true
117+ state.isAvailable === true;
120
118
121 const esvisible =
119 const esvisible =
122− props.networkType === NETWORK_NAMES.ROPSTEN ||
120+ props.networkType === NETWORK_NAMES.ROPSTEN ||
123− props.networkType === NETWORK_NAMES.MAINNET
121+ props.networkType === NETWORK_NAMES.MAINNET;
124
122
125 const esdomain =
123 const esdomain =
126− props.networkType === NETWORK_NAMES.ROPSTEN
124+ props.networkType === NETWORK_NAMES.ROPSTEN
127− ? "ropsten.etherscan.io"
125+ ? 'ropsten.etherscan.io'
128− : "etherscan.io"
126+ : 'etherscan.io';
129
127
130 return (
128 return (
131 <Row>
129 <Row>
132 <Col>
130 <Col>
133− <H1> { 'Create a Galaxy' } </H1>
131+ <H1> {'Create a Galaxy'} </H1>
134
132
135 <P>
133 <P>
136− {
137− 'Enter the galaxy to create and the address that will own ' +
134+ {'Enter the galaxy to create and the address that will own ' +
138− 'it (defaulting to this account, if not provided).'
135+ 'it (defaulting to this account, if not provided).'}
139− }
140 </P>
136 </P>
141
137
142 <GalaxyInput
138 <GalaxyInput
143− className='mono'
139+ className="mono"
144− prop-size='lg'
140+ prop-size="lg"
145− prop-format='innerLabel'
141+ prop-format="innerLabel"
146 autoFocus
142 autoFocus
147− placeholder='e.g. ~zod'
143+ placeholder="e.g. ~zod"
148 value={state.galaxyName}
144 value={state.galaxyName}
149 onChange={v => this.handleGalaxyNameInput(v)}>
145 onChange={v => this.handleGalaxyNameInput(v)}>
150− <InnerLabel>{ 'Galaxy Name' }</InnerLabel>
146+ <InnerLabel>{'Galaxy Name'}</InnerLabel>
151 <ValidatedSigil
147 <ValidatedSigil
152− className='tr-0 mt-05 mr-0 abs'
148+ className="tr-0 mt-05 mr-0 abs"
153 patp={state.galaxyName}
149 patp={state.galaxyName}
154 size={68}
150 size={68}
155− margin={8} />
151+ margin={8}
152+ />
156 </GalaxyInput>
153 </GalaxyInput>
157
154
158 <AddressInput
155 <AddressInput
159− className='mono mt-8'
156+ className="mono mt-8"
160− prop-size='lg'
157+ prop-size="lg"
161− prop-format='innerLabel'
158+ prop-format="innerLabel"
162− value={ state.galaxyOwner }
159+ value={state.galaxyOwner}
163− onChange={ v => this.handleAddressInput(v) }>
160+ onChange={v => this.handleAddressInput(v)}>
164− <InnerLabel>
165− { 'Address that will own this galaxy' }
161+ <InnerLabel>{'Address that will own this galaxy'}</InnerLabel>
166− </InnerLabel>
167− <ShowBlockie
168− className={'mt-1'}
169− address={state.galaxyOwner} />
162+ <ShowBlockie className={'mt-1'} address={state.galaxyOwner} />
170 </AddressInput>
163 </AddressInput>
171
164
172 <Anchor
165 <Anchor
@@ -175,12 +168,12 @@ class CreateGalaxy extends React.Component {
175 prop-disabled={!validAddress || !esvisible}
168 prop-disabled={!validAddress || !esvisible}
176 target={'_blank'}
169 target={'_blank'}
177 href={`https://${esdomain}/address/${state.galaxyOwner}`}>
170 href={`https://${esdomain}/address/${state.galaxyOwner}`}>
178− {'View on Etherscan ↗'}
171+ {'View on Etherscan ↗'}
179 </Anchor>
172 </Anchor>
180
173
181 <Button
174 <Button
182− prop-size='lg wide'
175+ prop-size="lg wide"
183− className='mt-8'
176+ className="mt-8"
184 prop-color={buttonTriState(state.isAvailable)}
177 prop-color={buttonTriState(state.isAvailable)}
185 disabled={!validGalaxy}
178 disabled={!validGalaxy}
186 onClick={() => this.confirmAvailability()}>
179 onClick={() => this.confirmAvailability()}>
@@ -193,15 +186,12 @@ class CreateGalaxy extends React.Component {
193 // Other
186 // Other
194 ref={this.statelessRef}
187 ref={this.statelessRef}
195 canGenerate={canGenerate}
188 canGenerate={canGenerate}
196− createUnsignedTxn={this.createUnsignedTxn} />
189+ createUnsignedTxn={this.createUnsignedTxn}
197−
190+ />
198 </Col>
191 </Col>
199 </Row>
192 </Row>
200− )
193+ );
201 }
194 }
202}
195}
203
196
204−
205−
206−
207−export default CreateGalaxy
197+export default CreateGalaxy;
src/bridge/views/GenKeyfile.js+56−67
@@ -1,27 +1,25 @@
1−import React from 'react'
1+import React from 'react';
2−import Maybe from 'folktale/maybe'
2+import Maybe from 'folktale/maybe';
3−import * as need from '../lib/need'
3+import * as need from '../lib/need';
4
4
5−import { Button } from '../components/Base'
5+import { Button } from '../components/Base';
6−import { Row, Col, H1, P } from '../components/Base'
6+import { Row, Col, H1, P } from '../components/Base';
7
7
8−import * as ob from 'urbit-ob'
8+import * as ob from 'urbit-ob';
9−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
9+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
10−import saveAs from 'file-saver'
10+import saveAs from 'file-saver';
11
11
12−import { attemptSeedDerivation, genKey } from '../lib/keys'
12+import { attemptSeedDerivation, genKey } from '../lib/keys';
13−import {
14− addHexPrefix
15− } from '../lib/wallet'
13+import { addHexPrefix } from '../lib/wallet';
16
14
17class GenKeyfile extends React.Component {
15class GenKeyfile extends React.Component {
18 constructor(props) {
16 constructor(props) {
19− super(props)
17+ super(props);
20
18
21 this.state = {
19 this.state = {
22 keyfile: '',
20 keyfile: '',
23− loaded: false
21+ loaded: false,
24− }
22+ };
25 }
23 }
26
24
27 getPointDetails() {
25 getPointDetails() {
@@ -31,116 +29,107 @@ class GenKeyfile extends React.Component {
31
29
32 // in case we did SetKeys earlier this session, make sure to generate the
30 // in case we did SetKeys earlier this session, make sure to generate the
33 // newer keyfile, rather than the one that will expire soon
31 // newer keyfile, rather than the one that will expire soon
32+ const revision =
34− const revision = (this.props.networkRevisionCache ||
33+ this.props.networkRevisionCache ||
35− parseInt(pointDetails.keyRevisionNumber));
34+ parseInt(pointDetails.keyRevisionNumber);
36
35
37 return {
36 return {
38 point,
37 point,
39 pointDetails,
38 pointDetails,
40− revision
39+ revision,
41− }
40+ };
42 }
41 }
43
42
44 async componentDidMount() {
43 async componentDidMount() {
45 const { point, pointDetails, revision } = this.getPointDetails();
44 const { point, pointDetails, revision } = this.getPointDetails();
46− let keyfile = ''
45+ let keyfile = '';
47
46
48− const hexRegExp = /[0-9A-Fa-f]{64}/g
47+ const hexRegExp = /[0-9A-Fa-f]{64}/g;
49− const networkSeed = await this.deriveSeed()
48+ const networkSeed = await this.deriveSeed();
50
49
51− const keysmatch = this.checkKeysMatch(networkSeed, pointDetails)
50+ const keysmatch = this.checkKeysMatch(networkSeed, pointDetails);
52− const seedValid = hexRegExp.test(networkSeed)
51+ const seedValid = hexRegExp.test(networkSeed);
53
52
54 if (keysmatch && seedValid) {
53 if (keysmatch && seedValid) {
55− keyfile = genKey(networkSeed, point, revision)
54+ keyfile = genKey(networkSeed, point, revision);
56 }
55 }
57
56
58 this.setState({
57 this.setState({
59 keyfile: keyfile,
58 keyfile: keyfile,
60− loaded: true
59+ loaded: true,
61 });
60 });
62 }
61 }
63
62
64 checkKeysMatch(networkSeed, pointDetails) {
63 checkKeysMatch(networkSeed, pointDetails) {
65− const crypub = pointDetails.encryptionKey
64+ const crypub = pointDetails.encryptionKey;
66− const sgnpub = pointDetails.authenticationKey
65+ const sgnpub = pointDetails.authenticationKey;
67
66
68− const { crypt, auth } = kg.deriveNetworkKeys(networkSeed)
67+ const { crypt, auth } = kg.deriveNetworkKeys(networkSeed);
69
68
70 const keysmatch =
69 const keysmatch =
71− crypub === addHexPrefix(crypt.public)
70+ crypub === addHexPrefix(crypt.public) &&
72− && sgnpub === addHexPrefix(auth.public)
71+ sgnpub === addHexPrefix(auth.public);
73
72
74 return keysmatch;
73 return keysmatch;
75 }
74 }
76
75
77 async deriveSeed() {
76 async deriveSeed() {
78− const next = false
77+ const next = false;
79− let seed = await attemptSeedDerivation(next, this.props)
78+ let seed = await attemptSeedDerivation(next, this.props);
80
79
81 if (seed.getOrElse('') === '' && this.props.networkSeedCache) {
80 if (seed.getOrElse('') === '' && this.props.networkSeedCache) {
82− seed = Maybe.Just(this.props.networkSeedCache)
81+ seed = Maybe.Just(this.props.networkSeedCache);
83 }
82 }
84
83
85− return seed.getOrElse('')
84+ return seed.getOrElse('');
86 }
85 }
87
86
88 render() {
87 render() {
89 const { point, revision } = this.getPointDetails();
88 const { point, revision } = this.getPointDetails();
90− const { keyfile, loaded } = this.state
89+ const { keyfile, loaded } = this.state;
91
90
92 return (
91 return (
93 <Row>
92 <Row>
94 <Col className={'col-md-8'}>
93 <Col className={'col-md-8'}>
95− <H1>{ 'Generate keyfile' }</H1>
94+ <H1>{'Generate keyfile'}</H1>
96
95
97− <P>
98− { "Download a private key file for booting this point in Arvo." }
96+ <P>{'Download a private key file for booting this point in Arvo.'}</P>
99− </P>
100
97
101− { keyfile === '' && !loaded &&
102− <P>
103− { "Generating keyfile..." }
98+ {keyfile === '' && !loaded && <P>{'Generating keyfile...'}</P>}
104− </P>
105− }
106
99
107− { keyfile === '' && loaded &&
100+ {keyfile === '' && loaded && (
108 <React.Fragment>
101 <React.Fragment>
109 <P>
102 <P>
110 <b>Warning: </b>
103 <b>Warning: </b>
111− { `No valid network seed detected. To generate a keyfile, you
104+ {`No valid network seed detected. To generate a keyfile, you
112 must reset your networking keys, or try logging in with your
105 must reset your networking keys, or try logging in with your
113− master ticket or management mnemonic.` }
106+ master ticket or management mnemonic.`}
114 </P>
107 </P>
115
108
116 <P>
109 <P>
117− { `If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.` }
110+ {`If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.`}
118 </P>
111 </P>
119 </React.Fragment>
112 </React.Fragment>
120− }
113+ )}
121
114
122− { keyfile !== '' &&
115+ {keyfile !== '' && (
123 <React.Fragment>
116 <React.Fragment>
124− <div className="pb-5 text-code keyfile">
117+ <div className="pb-5 text-code keyfile">{keyfile}</div>
125− { keyfile }
126− </div>
127 <Button
118 <Button
128− onClick={
119+ onClick={() => {
129− () => {
120+ let blob = new Blob([keyfile], {
130− let blob = new Blob([keyfile], {type:"text/plain;charset=utf-8"});
121+ type: 'text/plain;charset=utf-8',
122+ });
131− saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`)
123+ saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
132− }
133− }>
124+ }}>
134− Download →
125+ Download →
135− </Button>
126+ </Button>
136 </React.Fragment>
127 </React.Fragment>
137− }
128+ )}
138−
139 </Col>
129 </Col>
140 </Row>
130 </Row>
141− )
131+ );
142 }
132 }
143−
144}
133}
145
134
146−export default GenKeyfile
135+export default GenKeyfile;
src/bridge/views/InviteTicket.js+224−171
@@ -1,22 +1,36 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3+import {
4+ InnerLabel,
5+ Warning,
3−import { InnerLabel, Warning, TicketInput,
6+ TicketInput,
7+ VerifyTicketInput,
8+ Button,
9+ Row,
10+ Col,
11+ Passport,
4− VerifyTicketInput, Button, Row, Col, Passport } from '../components/Base'
12+} from '../components/Base';
5−import * as azimuth from 'azimuth-js'
13+import * as azimuth from 'azimuth-js';
6−import * as need from '../lib/need'
14+import * as need from '../lib/need';
7−
15+
8−import { randomPatq } from '../lib/lib'
16+import { randomPatq } from '../lib/lib';
9−import { ROUTE_NAMES } from '../lib/router'
17+import { ROUTE_NAMES } from '../lib/router';
18+import {
19+ DEFAULT_HD_PATH,
10−import { DEFAULT_HD_PATH, ownershipWalletFromTicket,
20+ ownershipWalletFromTicket,
11− walletFromMnemonic } from '../lib/wallet'
21+ walletFromMnemonic,
22+} from '../lib/wallet';
12−import { BRIDGE_ERROR } from '../lib/error'
23+import { BRIDGE_ERROR } from '../lib/error';
24+import {
25+ INVITE_STAGES,
26+ WALLET_STATES,
13−import { INVITE_STAGES, WALLET_STATES, TRANSACTION_STATES } from '../lib/invite'
27+ TRANSACTION_STATES,
28+} from '../lib/invite';
14−import { generateWallet, startTransactions } from '../lib/invite'
29+import { generateWallet, startTransactions } from '../lib/invite';
15
30
16class InviteTicket extends React.Component {
31class InviteTicket extends React.Component {
17−
18 constructor(props) {
32 constructor(props) {
19− super(props)
33+ super(props);
20
34
21 this.state = {
35 this.state = {
22 inviteTicket: '',
36 inviteTicket: '',
@@ -29,46 +43,46 @@ class InviteTicket extends React.Component {
29 walletStates: [],
43 walletStates: [],
30 errors: [],
44 errors: [],
31 stage: INVITE_STAGES.INVITE_LOGIN,
45 stage: INVITE_STAGES.INVITE_LOGIN,
32− transactionProgress: TRANSACTION_STATES.GENERATING
46+ transactionProgress: TRANSACTION_STATES.GENERATING,
33− }
47+ };
34
48
35− this.pointPlaceholder = randomPatq(4)
49+ this.pointPlaceholder = randomPatq(4);
36− this.ticketPlaceholder = randomPatq(12)
50+ this.ticketPlaceholder = randomPatq(12);
37
51
38− this.handleInviteTicketInput = this.handleInviteTicketInput.bind(this)
52+ this.handleInviteTicketInput = this.handleInviteTicketInput.bind(this);
39− this.handleVerifyTicketInput = this.handleVerifyTicketInput.bind(this)
53+ this.handleVerifyTicketInput = this.handleVerifyTicketInput.bind(this);
40− this.updateProgress = this.updateProgress.bind(this)
54+ this.updateProgress = this.updateProgress.bind(this);
41− this.pushWalletState = this.pushWalletState.bind(this)
55+ this.pushWalletState = this.pushWalletState.bind(this);
42− this.navigateLogin = this.navigateLogin.bind(this)
56+ this.navigateLogin = this.navigateLogin.bind(this);
43 }
57 }
44
58
45 pushWalletState(walletState) {
59 pushWalletState(walletState) {
46 this.setState({
60 this.setState({
47− walletStates: this.state.walletStates.concat(walletState)
61+ walletStates: this.state.walletStates.concat(walletState),
48− })
62+ });
49 }
63 }
50
64
51 navigateLogin() {
65 navigateLogin() {
52− this.props.popRoute()
66+ this.props.popRoute();
53− this.props.pushRoute(ROUTE_NAMES.NETWORK)
67+ this.props.pushRoute(ROUTE_NAMES.NETWORK);
54 }
68 }
55
69
56 handleInviteTicketInput(inviteTicket) {
70 handleInviteTicketInput(inviteTicket) {
57− this.setState({ inviteTicket })
71+ this.setState({ inviteTicket });
58 }
72 }
59
73
60 handleVerifyTicketInput(verifyTicket) {
74 handleVerifyTicketInput(verifyTicket) {
61− this.setState({ verifyTicket })
75+ this.setState({ verifyTicket });
62 }
76 }
63
77
64 updateTransactionProgress(transactionProgress) {
78 updateTransactionProgress(transactionProgress) {
65− this.setState({ transactionProgress })
79+ this.setState({ transactionProgress });
66 }
80 }
67
81
68 async unlockInviteWallet(inviteTicket) {
82 async unlockInviteWallet(inviteTicket) {
69 this.setState({
83 this.setState({
70 walletStates: this.state.walletStates.concat(WALLET_STATES.UNLOCKING),
84 walletStates: this.state.walletStates.concat(WALLET_STATES.UNLOCKING),
71− stage: INVITE_STAGES.INVITE_WALLET
85+ stage: INVITE_STAGES.INVITE_WALLET,
72 });
86 });
73
87
74 //NOTE ~zod because tmp wallet
88 //NOTE ~zod because tmp wallet
@@ -81,178 +95,208 @@ class InviteTicket extends React.Component {
81 ).matchWith({
95 ).matchWith({
82 Just: w => w.value,
96 Just: w => w.value,
83 Nothing: () => {
97 Nothing: () => {
84− throw BRIDGE_ERROR.MISSING_WALLET
98+ throw BRIDGE_ERROR.MISSING_WALLET;
85− }
99+ },
86 });
100 });
87
101
88 const addr = inviteWallet.address;
102 const addr = inviteWallet.address;
89
103
90 const ctrcs = need.contracts(this.props);
104 const ctrcs = need.contracts(this.props);
91
105
92− const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr)
106+ const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr);
93
107
94 this.setState({
108 this.setState({
95− walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING)
109+ walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING),
96− })
110+ });
97
111
98− let realPoint = Nothing()
112+ let realPoint = Nothing();
99− let realWallet = Nothing()
113+ let realWallet = Nothing();
100− let errors = []
114+ let errors = [];
101
115
102 if (incoming.length > 0) {
116 if (incoming.length > 0) {
103− let pointNum = parseInt(incoming[0], 10)
117+ let pointNum = parseInt(incoming[0], 10);
104− realPoint = Just(pointNum)
118+ realPoint = Just(pointNum);
105
119
106 this.setState({
120 this.setState({
107− realPoint
121+ realPoint,
108− })
122+ });
109
123
110− let genWallet = await generateWallet(pointNum)
124+ let genWallet = await generateWallet(pointNum);
111
125
112− realWallet = Just(genWallet)
126+ realWallet = Just(genWallet);
113 if (incoming.length > 1) {
127 if (incoming.length > 1) {
114 //TODO notify user "...and others / ticket reusable"
128 //TODO notify user "...and others / ticket reusable"
115 }
129 }
116 } else {
130 } else {
131+ errors = [
117− errors = ["Invite code has no claimable ship. Check your invite code and try again."]
132+ 'Invite code has no claimable ship. Check your invite code and try again.',
133+ ];
118 }
134 }
119
135
120 this.setState({
136 this.setState({
121 realWallet,
137 realWallet,
122 inviteWallet: Just(inviteWallet),
138 inviteWallet: Just(inviteWallet),
123 // dev mode: uncomment this out to autofill verified ticket
139 // dev mode: uncomment this out to autofill verified ticket
124− verifyTicket: !Nothing.hasInstance(realWallet) ? realWallet.value.ticket : '',
140+ verifyTicket: !Nothing.hasInstance(realWallet)
141+ ? realWallet.value.ticket
142+ : '',
125 walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING),
143 walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING),
126− errors
144+ errors,
127− })
145+ });
128 }
146 }
129
147
130 getTicketElement() {
148 getTicketElement() {
131− const phTick = this.ticketPlaceholder
149+ const phTick = this.ticketPlaceholder;
132
150
133− let isLogin = this.state.stage === INVITE_STAGES.INVITE_LOGIN
151+ let isLogin = this.state.stage === INVITE_STAGES.INVITE_LOGIN;
134− let isVerify = this.state.stage === INVITE_STAGES.INVITE_VERIFY
152+ let isVerify = this.state.stage === INVITE_STAGES.INVITE_VERIFY;
135
153
136 if (isLogin || isVerify) {
154 if (isLogin || isVerify) {
137−
138− let value = isLogin ? this.state.inviteTicket : this.state.verifyTicket
155+ let value = isLogin ? this.state.inviteTicket : this.state.verifyTicket;
156+ let handler = isLogin
139− let handler = isLogin ? this.handleInviteTicketInput : this.handleVerifyTicketInput
157+ ? this.handleInviteTicketInput
158+ : this.handleVerifyTicketInput;
140− let label = isLogin ? 'Activation code' : 'ticket'
159+ let label = isLogin ? 'Activation code' : 'ticket';
141
160
142 // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing
161 // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing
143 const Verified = this.state.realWallet.map(realWallet =>
162 const Verified = this.state.realWallet.map(realWallet =>
144 VerifyTicketInput(realWallet.ticket)
163 VerifyTicketInput(realWallet.ticket)
145− )
164+ );
146
165
147 // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing
166 // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing
148− const TicketElem =
167+ const TicketElem = isVerify
149− isVerify
150 ? Verified.getOrElse(TicketInput)
168 ? Verified.getOrElse(TicketInput)
151− : TicketInput
169+ : TicketInput;
152
170
153 return (
171 return (
154 <TicketElem
172 <TicketElem
155− className='mono mt-8'
173+ className="mono mt-8"
156− prop-size='md'
174+ prop-size="md"
157− prop-format='innerLabel'
175+ prop-format="innerLabel"
158− type='text'
176+ type="text"
159 autoFocus
177 autoFocus
160− name='invite'
178+ name="invite"
161− placeholder={ `e.g. ${phTick}` }
179+ placeholder={`e.g. ${phTick}`}
162− value={ value }
180+ value={value}
163− onChange={ handler }>
181+ onChange={handler}>
164− <InnerLabel>{ label }</InnerLabel>
182+ <InnerLabel>{label}</InnerLabel>
165 </TicketElem>
183 </TicketElem>
166− )
184+ );
167 } else {
185 } else {
168− return null
186+ return null;
169 }
187 }
170 }
188 }
171
189
172 getStageText() {
190 getStageText() {
173− const { stage } = this.state
191+ const { stage } = this.state;
174
192
175 let progressWidth = this.state.transactionProgress.pct;
193 let progressWidth = this.state.transactionProgress.pct;
176
194
195+ const stageText =
177− const stageText = stage === INVITE_STAGES.INVITE_LOGIN
196+ stage === INVITE_STAGES.INVITE_LOGIN ? (
178− ? <h1 className="text-700 mt-15">Welcome</h1>
197+ <h1 className="text-700 mt-15">Welcome</h1>
179− : stage === INVITE_STAGES.INVITE_WALLET
198+ ) : stage === INVITE_STAGES.INVITE_WALLET ? (
180− ? (
199+ <div>
200+ <h1 className="fs-6 lh-8 mb-3">Passport</h1>
201+ <p>
202+ A passport is your digital identity. You will use your passport to
203+ access your true computer, send payments, and administer your
204+ identity. So naturally, you must keep this secure.
205+ </p>
206+ <p>
207+ After you’ve downloaded your passport, back up the ticket manually
208+ or store on a trusted device.
209+ </p>
210+ </div>
211+ ) : stage === INVITE_STAGES.INVITE_VERIFY ? (
212+ <div>
213+ <h1 className="fs-6 lh-8 mb-3">Verify Passport</h1>
214+ <p>
215+ Afer you download your passport, verify your custody. Your passport
216+ should be a folder of image files. One of them is your Master
217+ Ticket. Open it and enter the 4 word phrase below (with hyphens).
218+ </p>
219+ </div>
220+ ) : INVITE_STAGES.INVITE_TRANSACTIONS ? (
221+ this.state.transactionProgress.label ===
222+ TRANSACTION_STATES.DONE.label ? (
181 <div>
223 <div>
182− <h1 className="fs-6 lh-8 mb-3">Passport</h1>
224+ <h1 className="fs-6 lh-8 mb-3">
183− <p>A passport is your digital identity. You will use your passport to access your true computer, send payments, and administer your identity. So naturally, you must keep this secure.</p>
225+ <span>Success</span>
184− <p>After you’ve downloaded your passport, back up the ticket manually or store on a trusted device.</p>
226+ <span className="ml-4 green">✓</span>
227+ </h1>
228+ <p>Transferring to Bridge...</p>
185 </div>
229 </div>
186− )
230+ ) : (
187− : stage === INVITE_STAGES.INVITE_VERIFY
188− ? (
189 <div>
231 <div>
190− <h1 className="fs-6 lh-8 mb-3">Verify Passport</h1>
232+ <h1 className="fs-6 lh-8 mb-3">Submitting</h1>
191− <p>Afer you download your passport, verify your custody. Your passport should be a folder of image files. One of them is your Master Ticket. Open it and enter the 4 word phrase below (with hyphens).</p>
233+ <p className="mt-4 mb-4">
192− </div>
234+ This step can take up to five minutes. Please do not leave this
193− ) : INVITE_STAGES.INVITE_TRANSACTIONS
235+ page until the transactions are complete.
194− ? this.state.transactionProgress.label === TRANSACTION_STATES.DONE.label
236+ </p>
195− ? (
237+ <div className="passport-progress mb-2">
196− <div>
238+ <div
197− <h1 className="fs-6 lh-8 mb-3">
239+ className="passport-progress-filled"
198− <span>Success</span>
240+ style={{ width: progressWidth }}></div>
199− <span className="ml-4 green">✓</span>
200− </h1>
201− <p>Transferring to Bridge...</p>
202 </div>
241 </div>
203− )
204− : (
205− <div>
206− <h1 className="fs-6 lh-8 mb-3">Submitting</h1>
207− <p className="mt-4 mb-4">This step can take up to five minutes. Please do not leave this page until the transactions are complete.</p>
208− <div className="passport-progress mb-2">
209− <div className="passport-progress-filled" style={{width: progressWidth}}></div>
210− </div>
211− <div className="flex justify-between">
242+ <div className="flex justify-between">
212− <div className="text-mono text-sm lh-6 green-dark">{this.state.transactionProgress.label}</div>
243+ <div className="text-mono text-sm lh-6 green-dark">
244+ {this.state.transactionProgress.label}
213 </div>
245 </div>
214 </div>
246 </div>
215− )
247+ </div>
248+ )
216− : null
249+ ) : null;
217
250
218− return stageText
251+ return stageText;
219 }
252 }
220
253
221 updateProgress(notification) {
254 updateProgress(notification) {
255+ let newState =
222− let newState = notification.type === "progress"
256+ notification.type === 'progress'
223 ? { transactionProgress: notification.value }
257 ? { transactionProgress: notification.value }
224− : notification.type === "notify"
258+ : notification.type === 'notify'
225 ? { errors: [notification.value] }
259 ? { errors: [notification.value] }
226− : {}
260+ : {};
227
261
228− this.setState(newState)
262+ this.setState(newState);
229 }
263 }
230
264
231 getContinueButton() {
265 getContinueButton() {
232− const { realPoint, realWallet, inviteWallet, stage, walletStates,
266+ const {
267+ realPoint,
268+ realWallet,
269+ inviteWallet,
270+ stage,
271+ walletStates,
233− verifyTicket, inviteTicket } = this.state
272+ verifyTicket,
273+ inviteTicket,
274+ } = this.state;
234− const { web3, contracts, setUrbitWallet } = this.props
275+ const { web3, contracts, setUrbitWallet } = this.props;
235
276
236 const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY)
277 const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY)
237 ? 'green'
278 ? 'green'
238− : 'black'
279+ : 'black';
239
280
281+ const clickHandler =
240− const clickHandler = stage === INVITE_STAGES.INVITE_LOGIN
282+ stage === INVITE_STAGES.INVITE_LOGIN
241 ? () => {
283 ? () => {
242− this.unlockInviteWallet(inviteTicket)
284+ this.unlockInviteWallet(inviteTicket);
243 }
285 }
244 : stage === INVITE_STAGES.INVITE_WALLET
286 : stage === INVITE_STAGES.INVITE_WALLET
245 ? () => {
287 ? () => {
246 this.setState({
288 this.setState({
247− stage: INVITE_STAGES.INVITE_VERIFY
289+ stage: INVITE_STAGES.INVITE_VERIFY,
248− })
290+ });
249 }
291 }
250 : stage === INVITE_STAGES.INVITE_VERIFY
292 : stage === INVITE_STAGES.INVITE_VERIFY
251 ? () => {
293 ? () => {
252 this.setState({
294 this.setState({
253 stage: INVITE_STAGES.INVITE_TRANSACTIONS,
295 stage: INVITE_STAGES.INVITE_TRANSACTIONS,
254− walletStates: this.state.walletStates.concat(WALLET_STATES.TRANSACTIONS)
296+ walletStates: this.state.walletStates.concat(
297+ WALLET_STATES.TRANSACTIONS
298+ ),
255− })
299+ });
256
300
257 startTransactions({
301 startTransactions({
258 inviteWalletM: inviteWallet,
302 inviteWalletM: inviteWallet,
@@ -262,77 +306,87 @@ class InviteTicket extends React.Component {
262 web3M: web3,
306 web3M: web3,
263 contractsM: contracts,
307 contractsM: contracts,
264 setUrbitWallet,
308 setUrbitWallet,
265− updateProgress: this.updateProgress
309+ updateProgress: this.updateProgress,
266− })
267− .then(() => {
310+ }).then(() => {
268 setTimeout(() => {
311 setTimeout(() => {
269− this.props.popRoute()
312+ this.props.popRoute();
270− }, 5000)
313+ }, 5000);
271− })
314+ });
272 }
315 }
273− : null
316+ : null;
274
317
275 const ticketMatches = this.state.realWallet.matchWith({
318 const ticketMatches = this.state.realWallet.matchWith({
276 Just: wal => wal.value.ticket === this.state.verifyTicket,
319 Just: wal => wal.value.ticket === this.state.verifyTicket,
277− Nothing: () => false
320+ Nothing: () => false,
278− })
321+ });
279
322
323+ const continueReady =
280− const continueReady = stage === INVITE_STAGES.INVITE_WALLET
324+ stage === INVITE_STAGES.INVITE_WALLET
281 ? walletStates.includes(WALLET_STATES.DOWNLOADED)
325 ? walletStates.includes(WALLET_STATES.DOWNLOADED)
282 : stage === INVITE_STAGES.INVITE_VERIFY
326 : stage === INVITE_STAGES.INVITE_VERIFY
283 ? ticketMatches
327 ? ticketMatches
284− : true
328+ : true;
285−
329+
330+ const buttonElem =
286− const buttonElem = stage === INVITE_STAGES.INVITE_TRANSACTIONS
331+ stage === INVITE_STAGES.INVITE_TRANSACTIONS ? null : (
287− ? null
288− : (
289− <Button
332+ <Button
290− className={'mt-4'}
333+ className={'mt-4'}
291− prop-size={'lg'}
334+ prop-size={'lg'}
292− prop-color={btnColor}
335+ prop-color={btnColor}
293− disabled={!continueReady}
336+ disabled={!continueReady}
294− onClick={clickHandler}
337+ onClick={clickHandler}>
295− >
296− { 'Continue →' }
338+ {'Continue →'}
297− </Button>
339+ </Button>
298− )
340+ );
299−
341+
300− return buttonElem
342+ return buttonElem;
301 }
343 }
302
344
303 getStageDisplay() {
345 getStageDisplay() {
304− const { stage } = this.state
346+ const { stage } = this.state;
305
347
306− if (stage === INVITE_STAGES.INVITE_LOGIN) return null
348+ if (stage === INVITE_STAGES.INVITE_LOGIN) return null;
307
349
308 return (
350 return (
309 <div className="mt-6 mb-8">
351 <div className="mt-6 mb-8">
352+ <span
310− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET && 'gray-20'}`}>1 Passport</span>
353+ className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET &&
354+ 'gray-20'}`}>
355+ 1 Passport
356+ </span>
357+ <span
311− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY && 'gray-20'}`}>2 Verify</span>
358+ className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY &&
359+ 'gray-20'}`}>
360+ 2 Verify
361+ </span>
362+ <span
363+ className={`fs-35 mr-3 ${stage !==
312− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_TRANSACTIONS && 'gray-20'}`}>3 Invite</span>
364+ INVITE_STAGES.INVITE_TRANSACTIONS && 'gray-20'}`}>
365+ 3 Invite
366+ </span>
313 </div>
367 </div>
314− )
368+ );
315 }
369 }
316
370
317 getErrors() {
371 getErrors() {
318− if (this.state.errors.length === 0) return null
372+ if (this.state.errors.length === 0) return null;
319
373
320− let errorElems = this.state.errors.map(e => <span>{e}</span>)
374+ let errorElems = this.state.errors.map(e => <span>{e}</span>);
321
375
322 return (
376 return (
323 <Warning>
377 <Warning>
324 <h3 className={'mb-2'}>{'Warning'}</h3>
378 <h3 className={'mb-2'}>{'Warning'}</h3>
325− { errorElems }
379+ {errorElems}
326 </Warning>
380 </Warning>
327− )
381+ );
328 }
382 }
329
383
330 render() {
384 render() {
331− let stageDisplay = this.getStageDisplay()
385+ let stageDisplay = this.getStageDisplay();
332− let stageText = this.getStageText()
386+ let stageText = this.getStageText();
333− let ticketElem = this.getTicketElement()
387+ let ticketElem = this.getTicketElement();
334− let continueButton = this.getContinueButton()
388+ let continueButton = this.getContinueButton();
335− let errorDisplay = this.getErrors()
389+ let errorDisplay = this.getErrors();
336
390
337 return (
391 return (
338 <Row>
392 <Row>
@@ -343,16 +397,15 @@ class InviteTicket extends React.Component {
343 {ticketElem}
397 {ticketElem}
344 {continueButton}
398 {continueButton}
345
399
346− { this.state.stage === INVITE_STAGES.INVITE_LOGIN &&
400+ {this.state.stage === INVITE_STAGES.INVITE_LOGIN && (
347 <Button
401 <Button
348 prop-type={`link`}
402 prop-type={`link`}
349 prop-size={`sm`}
403 prop-size={`sm`}
350 className={`block mt-4`}
404 className={`block mt-4`}
351− onClick={this.navigateLogin} >
405+ onClick={this.navigateLogin}>
352 {`Log in`}
406 {`Log in`}
353 </Button>
407 </Button>
354− }
408+ )}
355−
356 </Col>
409 </Col>
357 <Col className="col-md-offset-3 col-md-5 mt-18">
410 <Col className="col-md-offset-3 col-md-5 mt-18">
358 <Passport
411 <Passport
@@ -363,8 +416,8 @@ class InviteTicket extends React.Component {
363 />
416 />
364 </Col>
417 </Col>
365 </Row>
418 </Row>
366− )
419+ );
367 }
420 }
368}
421}
369
422
370−export default InviteTicket
423+export default InviteTicket;
src/bridge/views/InvitesManage.js+76−69
@@ -1,18 +1,23 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as ob from 'urbit-ob'
3+import * as ob from 'urbit-ob';
4−import * as azimuth from 'azimuth-js'
4+import * as azimuth from 'azimuth-js';
5−import * as need from '../lib/need'
5+import * as need from '../lib/need';
6−
6+
7+import {
8+ Row,
9+ Col,
7−import { Row, Col, Warning, Input,
10+ Warning,
11+ Input,
12+ PointInput,
13+ InnerLabel,
14+ ValidatedSigil,
8− PointInput, InnerLabel, ValidatedSigil } from '../components/Base'
15+} from '../components/Base';
9−import StatelessTransaction from '../components/StatelessTransaction'
16+import StatelessTransaction from '../components/StatelessTransaction';
10−
11
17
12class InvitesManage extends React.Component {
18class InvitesManage extends React.Component {
13−
14 constructor(props) {
19 constructor(props) {
15− super(props)
20+ super(props);
16
21
17 this.state = {
22 this.state = {
18 invitesWillWork: Nothing(),
23 invitesWillWork: Nothing(),
@@ -20,8 +25,8 @@ class InvitesManage extends React.Component {
20 targetPlanet: Nothing(),
25 targetPlanet: Nothing(),
21 currentPoolSize: Nothing(),
26 currentPoolSize: Nothing(),
22 cachedPoolSizes: {},
27 cachedPoolSizes: {},
23− targetPoolSize: 5
28+ targetPoolSize: 5,
24− }
29+ };
25
30
26 this.checkCurrentInvites = this.checkCurrentInvites.bind(this);
31 this.checkCurrentInvites = this.checkCurrentInvites.bind(this);
27 this.handlePointInput = this.handlePointInput.bind(this);
32 this.handlePointInput = this.handlePointInput.bind(this);
@@ -35,13 +40,15 @@ class InvitesManage extends React.Component {
35 this.point = need.pointCursor(this.props);
40 this.point = need.pointCursor(this.props);
36 this.contracts = need.contracts(this.props);
41 this.contracts = need.contracts(this.props);
37
42
38− azimuth.azimuth.isSpawnProxy(
43+ azimuth.azimuth
44+ .isSpawnProxy(
39− this.contracts,
45+ this.contracts,
40− this.point,
46+ this.point,
41− this.contracts.delegatedSending.address
47+ this.contracts.delegatedSending.address
48+ )
42− ).then(isSpawnProxy => {
49+ .then(isSpawnProxy => {
43− this.setState({invitesWillWork:Just(isSpawnProxy)});
50+ this.setState({ invitesWillWork: Just(isSpawnProxy) });
44− });
51+ });
45 }
52 }
46
53
47 componentDidUpdate(prevProps) {
54 componentDidUpdate(prevProps) {
@@ -53,10 +60,9 @@ class InvitesManage extends React.Component {
53 targetPlanet: Nothing(),
60 targetPlanet: Nothing(),
54 planetInput: targetPlanet,
61 planetInput: targetPlanet,
55 canGenerate: false,
62 canGenerate: false,
56− currentPoolSize: Nothing()
63+ currentPoolSize: Nothing(),
57− }
64+ };
58− if ((targetPlanet.length === 14)
59− && ob.isValidPatp(targetPlanet)) {
65+ if (targetPlanet.length === 14 && ob.isValidPatp(targetPlanet)) {
60 const target = ob.patp2dec(targetPlanet);
66 const target = ob.patp2dec(targetPlanet);
61 //TODO there must be a simpler way to do this check
67 //TODO there must be a simpler way to do this check
62 const isChild = ob.sein(targetPlanet) === ob.patp(this.point);
68 const isChild = ob.sein(targetPlanet) === ob.patp(this.point);
@@ -74,12 +80,12 @@ class InvitesManage extends React.Component {
74 if (typeof cachedPoolSizes[who] !== 'undefined') {
80 if (typeof cachedPoolSizes[who] !== 'undefined') {
75 return Just(cachedPoolSizes[who]);
81 return Just(cachedPoolSizes[who]);
76 } else {
82 } else {
77− azimuth.delegatedSending.getPool(this.contracts, who)
83+ azimuth.delegatedSending.getPool(this.contracts, who).then(pool => {
78− .then(pool => {
84+ azimuth.delegatedSending
79− azimuth.delegatedSending.invitesInPool(this.contracts, pool, this.point)
85+ .invitesInPool(this.contracts, pool, this.point)
80− .then(size => {
86+ .then(size => {
81− this.updatePoolSize(who, size);
87+ this.updatePoolSize(who, size);
82− });
88+ });
83 });
89 });
84 return Nothing();
90 return Nothing();
85 }
91 }
@@ -90,75 +96,76 @@ class InvitesManage extends React.Component {
90 cache[who] = size;
96 cache[who] = size;
91 this.setState({ cachedPoolSizes: cache });
97 this.setState({ cachedPoolSizes: cache });
92 const target = this.state.targetPlanet;
98 const target = this.state.targetPlanet;
93− const match = Just.hasInstance(target) ? (who === target.value) : false;
99+ const match = Just.hasInstance(target) ? who === target.value : false;
94 if (match) {
100 if (match) {
95 this.setState({ currentPoolSize: Just(size) });
101 this.setState({ currentPoolSize: Just(size) });
96 }
102 }
97 }
103 }
98
104
99 handlePoolSizeInput(targetSize) {
105 handlePoolSizeInput(targetSize) {
100− this.setState({targetPoolSize: targetSize});
106+ this.setState({ targetPoolSize: targetSize });
101 }
107 }
102
108
103 createUnsignedTxn() {
109 createUnsignedTxn() {
110+ return Just(
104− return Just(azimuth.delegatedSending.setPoolSize(
111+ azimuth.delegatedSending.setPoolSize(
105− this.contracts,
112+ this.contracts,
106− this.point,
113+ this.point,
107− this.state.targetPlanet.value,
114+ this.state.targetPlanet.value,
108− this.state.targetPoolSize
115+ this.state.targetPoolSize
109− ))
116+ )
117+ );
110 }
118 }
111
119
112 render() {
120 render() {
113−
114 let spawnProxyWarning = null;
121 let spawnProxyWarning = null;
115 if (this.state.invitesWillWork.value === false) {
122 if (this.state.invitesWillWork.value === false) {
116 spawnProxyWarning = (
123 spawnProxyWarning = (
117 <Warning>
124 <Warning>
118 <h3 className={'mb-2'}>{'Warning'}</h3>
125 <h3 className={'mb-2'}>{'Warning'}</h3>
119− {
120− 'Planets under this star will not be able to send invites ' +
126+ {'Planets under this star will not be able to send invites ' +
121− 'until the invite contract ('
127+ 'until the invite contract ('}
122− }<code>{this.contracts.delegatedSending.address}</code>{
128+ <code>{this.contracts.delegatedSending.address}</code>
123− ') is made spawn proxy for this star.'
129+ {') is made spawn proxy for this star.'}
124− }
125 </Warning>
130 </Warning>
126 );
131 );
127 }
132 }
128
133
129 let poolSizeText = this.state.currentPoolSize.matchWith({
134 let poolSizeText = this.state.currentPoolSize.matchWith({
130 Just: ps => `(currently ${ps.value})`,
135 Just: ps => `(currently ${ps.value})`,
131− Nothing: _ => ''
136+ Nothing: _ => '',
132 });
137 });
133
138
134 return (
139 return (
135 <Row>
140 <Row>
136 <Col>
141 <Col>
142+ <p>
143+ {
144+ 'manage invites here, for stars. can only give invites to child planets'
145+ }
146+ </p>
137
147
138− <p>{ 'manage invites here, for stars. can only give invites to child planets' }</p>
139−
140− { spawnProxyWarning }
148+ {spawnProxyWarning}
141
149
142 <PointInput
150 <PointInput
143− prop-format='innerlabel'
151+ prop-format="innerlabel"
144− prop-size='lg'
152+ prop-size="lg"
145− placeholder={ '~sampel-sipnem' }
153+ placeholder={'~sampel-sipnem'}
146− value={ this.state.planetInput }
154+ value={this.state.planetInput}
147− onChange={ this.handlePointInput }>
155+ onChange={this.handlePointInput}>
148− <InnerLabel>{ 'Planet to set invites for' }</InnerLabel>
156+ <InnerLabel>{'Planet to set invites for'}</InnerLabel>
149 <ValidatedSigil
157 <ValidatedSigil
150− patp={ this.state.targetPlanet }
158+ patp={this.state.targetPlanet}
151− validator={() => this.validatePoint} />
159+ validator={() => this.validatePoint}
160+ />
152 </PointInput>
161 </PointInput>
153
162
154 <Input
163 <Input
155− prop-format='innerlabel'
164+ prop-format="innerlabel"
156− prop-size='lg'
165+ prop-size="lg"
157− value={ this.state.targetPoolSize }
166+ value={this.state.targetPoolSize}
158− onChange={ this.handlePoolSizeInput }>
167+ onChange={this.handlePoolSizeInput}>
159− <InnerLabel>
160− { `Available invites ${poolSizeText}` }
168+ <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
161− </InnerLabel>
162 </Input>
169 </Input>
163
170
164 <StatelessTransaction
171 <StatelessTransaction
@@ -167,12 +174,12 @@ class InvitesManage extends React.Component {
167 // Other
174 // Other
168 canGenerate={this.state.canGenerate}
175 canGenerate={this.state.canGenerate}
169 createUnsignedTxn={this.createUnsignedTxn}
176 createUnsignedTxn={this.createUnsignedTxn}
170− ref={this.statelessRef} />
177+ ref={this.statelessRef}
171−
178+ />
172 </Col>
179 </Col>
173 </Row>
180 </Row>
174− )
181+ );
175 }
182 }
176}
183}
177
184
178−export default InvitesManage
185+export default InvitesManage;
src/bridge/views/InvitesSend.js+154−129
@@ -1,13 +1,13 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as azimuth from 'azimuth-js'
3+import * as azimuth from 'azimuth-js';
4−import * as need from '../lib/need'
4+import * as need from '../lib/need';
5
5
6−import { hasReceived, sendMail } from '../lib/inviteMail'
6+import { hasReceived, sendMail } from '../lib/inviteMail';
7−import { Row, Col, Button, Input, H3, Warning } from '../components/Base'
7+import { Row, Col, Button, Input, H3, Warning } from '../components/Base';
8
8
9// for wallet generation
9// for wallet generation
10−import * as wg from '../../walletgen/lib/lib'
10+import * as wg from '../../walletgen/lib/lib';
11
11
12// for transaction generation and signing
12// for transaction generation and signing
13import {
13import {
@@ -16,9 +16,9 @@ import {
16 waitForTransactionConfirm,
16 waitForTransactionConfirm,
17 hexify,
17 hexify,
18 fromWei,
18 fromWei,
19− toWei
19+ toWei,
20−} from '../lib/txn'
20+} from '../lib/txn';
21−import * as tank from '../lib/tank'
21+import * as tank from '../lib/tank';
22
22
23const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
23const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
24const GAS_LIMIT = 350000;
24const GAS_LIMIT = 350000;
@@ -31,19 +31,18 @@ const STATUS = {
31 FUNDING: 'Funding invites',
31 FUNDING: 'Funding invites',
32 SENDING: 'Sending invites',
32 SENDING: 'Sending invites',
33 DONE: 'Done',
33 DONE: 'Done',
34− FAIL: 'Some failed'
34+ FAIL: 'Some failed',
35−}
35+};
36
36
37const EMAIL_STATUS = {
37const EMAIL_STATUS = {
38 BUSY: '⋯',
38 BUSY: '⋯',
39 DONE: '✔',
39 DONE: '✔',
40− FAIL: '×'
40+ FAIL: '×',
41−}
41+};
42
42
43class InvitesSend extends React.Component {
43class InvitesSend extends React.Component {
44−
45 constructor(props) {
44 constructor(props) {
46− super(props)
45+ super(props);
47
46
48 this.state = {
47 this.state = {
49 invitesAvailable: Nothing(),
48 invitesAvailable: Nothing(),
@@ -53,8 +52,8 @@ class InvitesSend extends React.Component {
53 errors: Nothing(),
52 errors: Nothing(),
54 //
53 //
55 fundingMessage: Nothing(),
54 fundingMessage: Nothing(),
56− wipInvites: []
55+ wipInvites: [],
57− }
56+ };
58
57
59 this.findInvited = this.findInvited.bind(this);
58 this.findInvited = this.findInvited.bind(this);
60 this.generateInvites = this.generateInvites.bind(this);
59 this.generateInvites = this.generateInvites.bind(this);
@@ -75,10 +74,11 @@ class InvitesSend extends React.Component {
75 this.web3 = need.web3(this.props);
74 this.web3 = need.web3(this.props);
76
75
77 console.log(azimuth.delegatedSending);
76 console.log(azimuth.delegatedSending);
77+ azimuth.delegatedSending
78− azimuth.delegatedSending.getTotalUsableInvites(this.contracts, this.point)
78+ .getTotalUsableInvites(this.contracts, this.point)
79− .then(count => {
79+ .then(count => {
80− this.setState({invitesAvailable: Just(count)});
80+ this.setState({ invitesAvailable: Just(count) });
81− });
81+ });
82 this.findInvited();
82 this.findInvited();
83 }
83 }
84
84
@@ -93,19 +93,19 @@ class InvitesSend extends React.Component {
93 );
93 );
94 invited = invited.map(async point => {
94 invited = invited.map(async point => {
95 const active = await azimuth.azimuth.isActive(this.contracts, point);
95 const active = await azimuth.azimuth.isActive(this.contracts, point);
96− const res = {point: Number(point), active:active};
96+ const res = { point: Number(point), active: active };
97 return res;
97 return res;
98 });
98 });
99 invited = await Promise.all(invited);
99 invited = await Promise.all(invited);
100 const total = invited.length;
100 const total = invited.length;
101 const accepted = invited.filter(i => i.active).length;
101 const accepted = invited.filter(i => i.active).length;
102− this.setState({invited: Just({total, accepted})});
102+ this.setState({ invited: Just({ total, accepted }) });
103 }
103 }
104
104
105 async getTemporaryWallet() {
105 async getTemporaryWallet() {
106 const ticket = await wg.makeTicket(0x10000); // planet-sized ticket
106 const ticket = await wg.makeTicket(0x10000); // planet-sized ticket
107 const owner = await wg.generateOwnershipWallet(0, ticket);
107 const owner = await wg.generateOwnershipWallet(0, ticket);
108− return {ticket, owner: owner.keys.address};
108+ return { ticket, owner: owner.keys.address };
109 }
109 }
110
110
111 async generateInvites() {
111 async generateInvites() {
@@ -114,13 +114,15 @@ class InvitesSend extends React.Component {
114 const web3 = this.web3;
114 const web3 = this.web3;
115 const targets = this.state.targets;
115 const targets = this.state.targets;
116 const planets = await azimuth.delegatedSending.getPlanetsToSend(
116 const planets = await azimuth.delegatedSending.getPlanetsToSend(
117− this.contracts, this.point, targets.length
117+ this.contracts,
118+ this.point,
119+ targets.length
118 );
120 );
119
121
120 // account for the race condition where invites got used up while we were
122 // account for the race condition where invites got used up while we were
121 // composing our target list
123 // composing our target list
122 if (planets.length < targets.length) {
124 if (planets.length < targets.length) {
123− this.addError('Can currently only send '+planets.length+' invites!');
125+ this.addError('Can currently only send ' + planets.length + ' invites!');
124 return;
126 return;
125 }
127 }
126
128
@@ -137,7 +139,10 @@ class InvitesSend extends React.Component {
137 const wallet = await this.getTemporaryWallet();
139 const wallet = await this.getTemporaryWallet();
138
140
139 let inviteTx = azimuth.delegatedSending.sendPoint(
141 let inviteTx = azimuth.delegatedSending.sendPoint(
140− this.contracts, this.point, planets[i], wallet.owner
142+ this.contracts,
143+ this.point,
144+ planets[i],
145+ wallet.owner
141 );
146 );
142 const signedTx = await signTransaction({
147 const signedTx = await signTransaction({
143 ...this.props,
148 ...this.props,
@@ -146,11 +151,11 @@ class InvitesSend extends React.Component {
146 gasLimit: GAS_LIMIT.toString(),
151 gasLimit: GAS_LIMIT.toString(),
147 nonce: nonce + i,
152 nonce: nonce + i,
148 chainId: chainId,
153 chainId: chainId,
149− setStx: () => {}
154+ setStx: () => {},
150 });
155 });
151 const rawTx = hexify(signedTx.serialize());
156 const rawTx = hexify(signedTx.serialize());
152
157
153− invites.push({email, ticket: wallet.ticket, signedTx, rawTx });
158+ invites.push({ email, ticket: wallet.ticket, signedTx, rawTx });
154 this.setEmailStatus(i, EMAIL_STATUS.DONE);
159 this.setEmailStatus(i, EMAIL_STATUS.DONE);
155 }
160 }
156
161
@@ -164,14 +169,21 @@ class InvitesSend extends React.Component {
164 //TODO invite status isn't changing/re-rendering... why?
169 //TODO invite status isn't changing/re-rendering... why?
165 this.setState({
170 this.setState({
166 status: STATUS.FUNDING,
171 status: STATUS.FUNDING,
172+ invites: invites.map(i => {
167− invites: invites.map(i => { return {...i, status: Nothing()}; })
173+ return { ...i, status: Nothing() };
174+ }),
168 });
175 });
169
176
170 const tankWasUsed = await tank.ensureFundsFor(
177 const tankWasUsed = await tank.ensureFundsFor(
178+ web3,
179+ this.point,
171− web3, this.point, this.address,
180+ this.address,
172− (INVITE_COST * invites.length),
181+ INVITE_COST * invites.length,
173− invites.map(i => i.rawTx), this.askForFunding,
182+ invites.map(i => i.rawTx),
183+ this.askForFunding,
184+ () => {
174− () => { this.setState({ fundingMessage: Nothing() }); }
185+ this.setState({ fundingMessage: Nothing() });
186+ }
175 );
187 );
176
188
177 this.setState({ status: STATUS.SENDING });
189 this.setState({ status: STATUS.SENDING });
@@ -185,31 +197,38 @@ class InvitesSend extends React.Component {
185 let txHash;
197 let txHash;
186 try {
198 try {
187 txHash = await sendSignedTransaction(
199 txHash = await sendSignedTransaction(
200+ web3,
188− web3, Just(invite.signedTx), tankWasUsed, ()=>{}
201+ Just(invite.signedTx),
202+ tankWasUsed,
203+ () => {}
189 );
204 );
190− } catch(err) {
205+ } catch (err) {
191 console.error('invite tx sending failed', err);
206 console.error('invite tx sending failed', err);
192 this.addError('Invite transaction not sent for ' + invite.email);
207 this.addError('Invite transaction not sent for ' + invite.email);
193 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
208 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
194 continue;
209 continue;
195− };
210+ }
196
211
197 // ask the email sender to send this. we can do this prior to tx confirm,
212 // ask the email sender to send this. we can do this prior to tx confirm,
198 // because the sender service waits for confirm & success for us.
213 // because the sender service waits for confirm & success for us.
199 // if this fails, inform the user and skip to next
214 // if this fails, inform the user and skip to next
200 try {
215 try {
201 const mailSuccess = await sendMail(
216 const mailSuccess = await sendMail(
217+ invite.email,
202− invite.email, invite.ticket, invite.rawTx
218+ invite.ticket,
219+ invite.rawTx
203 );
220 );
204 if (!mailSuccess) throw new Error();
221 if (!mailSuccess) throw new Error();
205 this.setEmailStatus(i, EMAIL_STATUS.DONE);
222 this.setEmailStatus(i, EMAIL_STATUS.DONE);
206− } catch(e) {
223+ } catch (e) {
207 console.log('email send failed');
224 console.log('email send failed');
208 //NOTE this assumes that the transaction succeeds, but we don't know
225 //NOTE this assumes that the transaction succeeds, but we don't know
209 // that for a fact yet...
226 // that for a fact yet...
210 this.addError(
227 this.addError(
211− 'Invite email failed to send for ' + invite.email +
228+ 'Invite email failed to send for ' +
229+ invite.email +
212− '. Please give them this ticket: ' + invite.ticket
230+ '. Please give them this ticket: ' +
231+ invite.ticket
213 );
232 );
214 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
233 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
215 continue;
234 continue;
@@ -217,8 +236,7 @@ class InvitesSend extends React.Component {
217
236
218 // update status on transaction confirm, but don't block on it
237 // update status on transaction confirm, but don't block on it
219 promises.push(
238 promises.push(
220− waitForTransactionConfirm(web3, txHash)
239+ waitForTransactionConfirm(web3, txHash).then(success => {
221− .then(success => {
222 if (success) {
240 if (success) {
223 this.setEmailStatus(i, EMAIL_STATUS.DONE);
241 this.setEmailStatus(i, EMAIL_STATUS.DONE);
224 } else {
242 } else {
@@ -229,7 +247,6 @@ class InvitesSend extends React.Component {
229 return success;
247 return success;
230 })
248 })
231 );
249 );
232−
233 }
250 }
234
251
235 const txStatus = await Promise.all(promises);
252 const txStatus = await Promise.all(promises);
@@ -249,17 +266,24 @@ class InvitesSend extends React.Component {
249 addError(error) {
266 addError(error) {
250 const newError = this.state.errors.matchWith({
267 const newError = this.state.errors.matchWith({
251 Nothing: () => [error],
268 Nothing: () => [error],
269+ Just: errs => {
252− Just: errs => { errs.value.push(error); return errs.value; }
270+ errs.value.push(error);
271+ return errs.value;
272+ },
253 });
273 });
254 this.setState({ errors: Just(newError) });
274 this.setState({ errors: Just(newError) });
255 }
275 }
256
276
257 askForFunding(address, minBalance, balance) {
277 askForFunding(address, minBalance, balance) {
278+ this.setState({
258− this.setState({ fundingMessage: Just(
279+ fundingMessage: Just(
259− `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
280+ `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
260− `we'll continue once that's true. Current balance: ${fromWei(balance)}.` +
281+ `we'll continue once that's true. Current balance: ${fromWei(
282+ balance
283+ )}.` +
261− `Waiting...`
284+ `Waiting...`
285+ ),
262− ) });
286+ });
263 }
287 }
264
288
265 handleEmailInput(i, email) {
289 handleEmailInput(i, email) {
@@ -268,8 +292,7 @@ class InvitesSend extends React.Component {
268
292
269 // if it's a valid address, check if it has received an invite before
293 // if it's a valid address, check if it has received an invite before
270 if (email.match(/.*@.*\...+/)) {
294 if (email.match(/.*@.*\...+/)) {
271− hasReceived(email)
295+ hasReceived(email).then(res => {
272− .then(res => {
273 targets[i].hasReceived = Just(res);
296 targets[i].hasReceived = Just(res);
274 this.setState({ targets });
297 this.setState({ targets });
275 });
298 });
@@ -284,7 +307,7 @@ class InvitesSend extends React.Component {
284 const targets = this.state.targets;
307 const targets = this.state.targets;
285
308
286 // may not have duplicate targets
309 // may not have duplicate targets
287− if ((new Set(targets.map(t => t.email))).size !== targets.length)
310+ if (new Set(targets.map(t => t.email)).size !== targets.length)
288 return false;
311 return false;
289
312
290 // none may have received invites already
313 // none may have received invites already
@@ -292,14 +315,14 @@ class InvitesSend extends React.Component {
292 const target = targets[i];
315 const target = targets[i];
293 const res = target.hasReceived.matchWith({
316 const res = target.hasReceived.matchWith({
294 Nothing: () => true,
317 Nothing: () => true,
295− Just: has => has.value
318+ Just: has => has.value,
296 });
319 });
297 if (res) return false;
320 if (res) return false;
298 }
321 }
299
322
300 return this.state.invitesAvailable.matchWith({
323 return this.state.invitesAvailable.matchWith({
301 Nothing: () => false,
324 Nothing: () => false,
302− Just: invites => (invites.value > 0)
325+ Just: invites => invites.value > 0,
303 });
326 });
304 }
327 }
305
328
@@ -316,46 +339,50 @@ class InvitesSend extends React.Component {
316 }
339 }
317
340
318 render() {
341 render() {
319−
342+ const dif =
320− const dif = (this.state.status === STATUS.DONE)
321− ? this.state.targets.length
343+ this.state.status === STATUS.DONE ? this.state.targets.length : null;
322− : null;
323
344
324 const invitesAvailable = this.state.invitesAvailable.matchWith({
345 const invitesAvailable = this.state.invitesAvailable.matchWith({
325 Nothing: () => 'Loading...',
346 Nothing: () => 'Loading...',
326− Just: (invites) => {
347+ Just: invites => {
327 const dia = dif ? `(-${dif})` : '';
348 const dia = dif ? `(-${dif})` : '';
328 return `You currently have ${invites.value}${dia} invitations left.`;
349 return `You currently have ${invites.value}${dia} invitations left.`;
329− }
350+ },
330 });
351 });
331
352
332 const invitesSent = this.state.invited.matchWith({
353 const invitesSent = this.state.invited.matchWith({
333− Nothing: () => (<>{'Loading...'}</>),
354+ Nothing: () => <>{'Loading...'}</>,
334− Just: (invited) => {
355+ Just: invited => {
335 const dit = dif ? `(+${dif})` : '';
356 const dit = dif ? `(+${dif})` : '';
336− return (<>
357+ return (
358+ <>
337− {'Out of the '}
359+ {'Out of the '}
338− {invited.value.total + dit}
360+ {invited.value.total + dit}
339− {' invites you sent, '}
361+ {' invites you sent, '}
340− {invited.value.accepted}
362+ {invited.value.accepted}
341− {' have been accepted.'}
363+ {' have been accepted.'}
342− </>);
364+ </>
365+ );
343− }
366+ },
344 });
367 });
345
368
346 const error = this.state.errors.matchWith({
369 const error = this.state.errors.matchWith({
347 Nothing: () => null,
370 Nothing: () => null,
371+ Just: errors => (
348− Just: (errors) => (<Warning className={'mt-8'}>
372+ <Warning className={'mt-8'}>
349− <H3 style={{marginTop: 0, paddingTop: 0}}>
373+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
350− { 'Something went wrong!' }
374+ {'Something went wrong!'}
351− </H3>
375+ </H3>
352− { errors.value.map(err => (<p>{err}</p>)) }
376+ {errors.value.map(err => (
377+ <p>{err}</p>
378+ ))}
353− </Warning>)
379+ </Warning>
380+ ),
354 });
381 });
355
382
356 const fundingMessage = this.state.fundingMessage.matchWith({
383 const fundingMessage = this.state.fundingMessage.matchWith({
357 Nothing: () => null,
384 Nothing: () => null,
358− Just: msg => (<Warning className={'mt-8'}>{msg.value}</Warning>)
385+ Just: msg => <Warning className={'mt-8'}>{msg.value}</Warning>,
359 });
386 });
360
387
361 let inputs;
388 let inputs;
@@ -363,17 +390,15 @@ class InvitesSend extends React.Component {
363 //TODO proper clickable router thing
390 //TODO proper clickable router thing
364 inputs = '<- Back to Home';
391 inputs = '<- Back to Home';
365 } else {
392 } else {
366−
367− const inputDisabled = (this.state.status !== STATUS.INPUT);
393+ const inputDisabled = this.state.status !== STATUS.INPUT;
368
394
369 const lessButton = (
395 const lessButton = (
370 <Button
396 <Button
371 prop-size={'s narrow'}
397 prop-size={'s narrow'}
372 className={'mt-8'}
398 className={'mt-8'}
373 disabled={inputDisabled || this.state.targets.length === 1}
399 disabled={inputDisabled || this.state.targets.length === 1}
374− onClick={this.removeTarget}
400+ onClick={this.removeTarget}>
375− >
376− { '-' }
401+ {'-'}
377 </Button>
402 </Button>
378 );
403 );
379
404
@@ -385,12 +410,11 @@ class InvitesSend extends React.Component {
385 inputDisabled ||
410 inputDisabled ||
386 this.state.invitesAvailable.matchWith({
411 this.state.invitesAvailable.matchWith({
387 Nothing: () => true,
412 Nothing: () => true,
388− Just: (inv) => (inv.value <= this.state.targets.length)
413+ Just: inv => inv.value <= this.state.targets.length,
389 })
414 })
390 }
415 }
391− onClick={this.addTarget}
416+ onClick={this.addTarget}>
392− >
393− { '+' }
417+ {'+'}
394 </Button>
418 </Button>
395 );
419 );
396
420
@@ -399,70 +423,71 @@ class InvitesSend extends React.Component {
399 const target = this.state.targets[i];
423 const target = this.state.targets[i];
400 let progress = target.status.matchWith({
424 let progress = target.status.matchWith({
401 Nothing: () => '',
425 Nothing: () => '',
402− Just: status => status.value
426+ Just: status => status.value,
403 });
427 });
404− targetInputs.push(<>
428+ targetInputs.push(
429+ <>
405− <Input
430+ <Input
406− value={ target.email }
431+ value={target.email}
432+ onChange={value => {
407− onChange={ (value) => { this.handleEmailInput(i, value); } }
433+ this.handleEmailInput(i, value);
434+ }}
408− placeholder={'Email address'}
435+ placeholder={'Email address'}
409− disabled={ inputDisabled }>
436+ disabled={inputDisabled}></Input>
410− </Input>
411− {
412− target.hasReceived.matchWith({
437+ {target.hasReceived.matchWith({
413 Nothing: () => null,
438 Nothing: () => null,
439+ Just: has =>
414− Just: (has) => has.value ? <span>{'×'}</span> : <span>{'°'}</span>
440+ has.value ? <span>{'×'}</span> : <span>{'°'}</span>,
415− })
441+ })}
416− }
417− { progress }
442+ {progress}
418− </>);
443+ </>
444+ );
419 }
445 }
420
446
421 const submitButton = (
447 const submitButton = (
422 <Button
448 <Button
423 prop-size={'xl wide'}
449 prop-size={'xl wide'}
424 className={'mt-8'}
450 className={'mt-8'}
451+ disabled={
425− disabled={ (inputDisabled && (this.state.status !== STATUS.CAN_SEND))
452+ (inputDisabled && this.state.status !== STATUS.CAN_SEND) ||
426− || !this.canSend() }
453+ !this.canSend()
454+ }
427− onClick={ () => {
455+ onClick={() => {
428− if (this.state.status === STATUS.INPUT)
456+ if (this.state.status === STATUS.INPUT) this.generateInvites();
429− this.generateInvites();
430− if (this.state.status === STATUS.CAN_SEND)
457+ if (this.state.status === STATUS.CAN_SEND) this.sendInvites();
431− this.sendInvites();
432− }}
458+ }}>
433− >
434− { this.state.status }
459+ {this.state.status}
435 </Button>
460 </Button>
436 );
461 );
437
462
438− inputs = (<>
463+ inputs = (
464+ <>
439− { lessButton }
465+ {lessButton}
440− { moreButton }
466+ {moreButton}
441− { targetInputs }
467+ {targetInputs}
442− { submitButton }
468+ {submitButton}
443− </>);
469+ </>
470+ );
444 }
471 }
445
472
446 return (
473 return (
447 <Row>
474 <Row>
448 <Col>
475 <Col>
476+ <p>{'send invites here, for planets'}</p>
449
477
450− <p>{ 'send invites here, for planets' }</p>
478+ <p>{invitesAvailable}</p>
451
479
452− <p>{ invitesAvailable }</p>
480+ <ul>{invitesSent}</ul>
453
481
454− <ul>{ invitesSent }</ul>
482+ {error}
455
483
456− { error }
457−
458− { fundingMessage }
484+ {fundingMessage}
459−
460− { inputs }
461
485
486+ {inputs}
462 </Col>
487 </Col>
463 </Row>
488 </Row>
464− )
489+ );
465 }
490 }
466}
491}
467
492
468−export default InvitesSend
493+export default InvitesSend;
src/bridge/views/IssueChild.js+130−128
@@ -1,46 +1,53 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { azimuth, ecliptic } from 'azimuth-js'
3+import { azimuth, ecliptic } from 'azimuth-js';
4−import * as ob from 'urbit-ob'
4+import * as ob from 'urbit-ob';
5−import * as need from '../lib/need'
5+import * as need from '../lib/need';
6
6
7import {
7import {
8+ Row,
9+ Col,
10+ H1,
11+ P,
8− Row, Col, H1, P, Anchor,
12+ Anchor,
13+ ShowBlockie,
9− ShowBlockie, ValidatedSigil,
14+ ValidatedSigil,
15+ PointInput,
10− PointInput, AddressInput, InnerLabel
16+ AddressInput,
17+ InnerLabel,
11−} from '../components/Base'
18+} from '../components/Base';
12−
19+
13−import StatelessTransaction from '../components/StatelessTransaction'
20+import StatelessTransaction from '../components/StatelessTransaction';
14−
21+
15−import { NETWORK_NAMES } from '../lib/network'
22+import { NETWORK_NAMES } from '../lib/network';
16−import { getSpawnCandidate } from '../lib/child'
23+import { getSpawnCandidate } from '../lib/child';
17−import { canDecodePatp } from '../lib/txn'
24+import { canDecodePatp } from '../lib/txn';
18−
25+
19−import { isValidAddress } from '../lib/wallet'
26+import { isValidAddress } from '../lib/wallet';
20
27
21const setFind = (set, pred) => {
28const setFind = (set, pred) => {
22 for (const e of set) {
29 for (const e of set) {
23 if (pred(e)) {
30 if (pred(e)) {
24− return e
31+ return e;
25 }
32 }
26 }
33 }
27− return undefined
34+ return undefined;
28−}
35+};
29
36
30class IssueChild extends React.Component {
37class IssueChild extends React.Component {
31 constructor(props) {
38 constructor(props) {
32− super(props)
39+ super(props);
33
40
34 const issuingPoint = parseInt(need.pointCursor(props), 10);
41 const issuingPoint = parseInt(need.pointCursor(props), 10);
35
42
36− const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint))
43+ const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint));
37
44
38 const suggestions = [
45 const suggestions = [
39 getCandidate(),
46 getCandidate(),
40 getCandidate(),
47 getCandidate(),
41 getCandidate(),
48 getCandidate(),
42− getCandidate()
49+ getCandidate(),
43− ]
50+ ];
44
51
45 this.state = {
52 this.state = {
46 receivingAddress: '',
53 receivingAddress: '',
@@ -49,193 +56,187 @@ class IssueChild extends React.Component {
49 isAvailable: Nothing(), // use Nothing to allow attempt when offline
56 isAvailable: Nothing(), // use Nothing to allow attempt when offline
50 suggestions: suggestions,
57 suggestions: suggestions,
51 validChildren: new Set(),
58 validChildren: new Set(),
52− autoComplete: ''
59+ autoComplete: '',
53− }
60+ };
54
61
55− this.handlePointInput = this.handlePointInput.bind(this)
62+ this.handlePointInput = this.handlePointInput.bind(this);
56− this.handleAddressInput = this.handleAddressInput.bind(this)
63+ this.handleAddressInput = this.handleAddressInput.bind(this);
57− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
64+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
58 this.statelessRef = React.createRef();
65 this.statelessRef = React.createRef();
59 }
66 }
60
67
61− handleAddressInput = (receivingAddress) => {
68+ handleAddressInput = receivingAddress => {
62− this.setState({ receivingAddress })
69+ this.setState({ receivingAddress });
63− this.statelessRef.current.clearTxn()
70+ this.statelessRef.current.clearTxn();
64− }
71+ };
65
72
66 componentWillMount() {
73 componentWillMount() {
67− const { issuingPoint } = this.state
74+ const { issuingPoint } = this.state;
68
75
69 const validContracts = need.contracts(this.props);
76 const validContracts = need.contracts(this.props);
70
77
78+ azimuth
79+ .getUnspawnedChildren(validContracts, issuingPoint)
71− azimuth.getUnspawnedChildren(validContracts, issuingPoint).then(ps => this.setState({ validChildren: new Set(ps.map(ob.patp)) }))
80+ .then(ps => this.setState({ validChildren: new Set(ps.map(ob.patp)) }));
72 }
81 }
73
82
74 handlePointInput(desiredPoint) {
83 handlePointInput(desiredPoint) {
75 if (desiredPoint.length < 15) {
84 if (desiredPoint.length < 15) {
85+ const suggestedPoint =
86+ setFind(this.state.validChildren, e => e.startsWith(desiredPoint)) ||
87+ '';
88+ const rendered =
89+ ' '.repeat(desiredPoint.length) +
90+ suggestedPoint.substring(desiredPoint.length);
76
91
77− const suggestedPoint = setFind(this.state.validChildren, (e => e.startsWith(desiredPoint))) || ''
78− const rendered = ' '.repeat(desiredPoint.length) + suggestedPoint.substring(desiredPoint.length)
79−
80− const available = Just(this.state.validChildren.has(desiredPoint))
92+ const available = Just(this.state.validChildren.has(desiredPoint));
81
93
82 this.setState({
94 this.setState({
83 desiredPoint,
95 desiredPoint,
84 isAvailable: available,
96 isAvailable: available,
85− autoComplete: rendered
97+ autoComplete: rendered,
86− })
98+ });
87− this.statelessRef.current.clearTxn()
99+ this.statelessRef.current.clearTxn();
88−
89 }
100 }
90 }
101 }
91
102
92−
93 createUnsignedTxn() {
103 createUnsignedTxn() {
94− const { state, props } = this
104+ const { state, props } = this;
95
105
96− if (isValidAddress(state.receivingAddress) === false) return Nothing()
106+ if (isValidAddress(state.receivingAddress) === false) return Nothing();
97− if (state.isAvailable === false) return Nothing()
107+ if (state.isAvailable === false) return Nothing();
98− if (canDecodePatp(state.desiredPoint) === false) return Nothing()
108+ if (canDecodePatp(state.desiredPoint) === false) return Nothing();
99
109
100 const validContracts = need.contracts(props);
110 const validContracts = need.contracts(props);
101
111
102− const pointDec = ob.patp2dec(state.desiredPoint)
112+ const pointDec = ob.patp2dec(state.desiredPoint);
103
113
104 const txn = ecliptic.spawn(
114 const txn = ecliptic.spawn(
105 validContracts,
115 validContracts,
106 pointDec,
116 pointDec,
107 state.receivingAddress
117 state.receivingAddress
108− )
118+ );
109
119
110− return Just(txn)
120+ return Just(txn);
111 }
121 }
112
122
113−
114 validatePoint = patp => {
123 validatePoint = patp => {
115− const point = this.point
124+ const point = this.point;
116
125
117− let vpatp = false
126+ let vpatp = false;
118 try {
127 try {
119− vpatp = ob.isValidPatp(patp)
128+ vpatp = ob.isValidPatp(patp);
120− } catch(_) {
129+ } catch (_) {}
121− }
122
130
123− let vchild = false
131+ let vchild = false;
124 try {
132 try {
125− vchild = ob.sein(patp) === ob.patp(point)
133+ vchild = ob.sein(patp) === ob.patp(point);
126− } catch(_) {
134+ } catch (_) {}
127− }
128
135
129− return vpatp && vchild
136+ return vpatp && vchild;
130− }
137+ };
131
138
132 render() {
139 render() {
133− const { props, state } = this
140+ const { props, state } = this;
134
141
135− const validAddress = isValidAddress(state.receivingAddress)
142+ const validAddress = isValidAddress(state.receivingAddress);
136
143
137 const canGenerate = props.web3.matchWith({
144 const canGenerate = props.web3.matchWith({
138 Nothing: () => {
145 Nothing: () => {
139− return validAddress === true
146+ return validAddress === true;
140 },
147 },
141 Just: () => {
148 Just: () => {
142− return validAddress === true &&
143− state.isAvailable.value === true
149+ return validAddress === true && state.isAvailable.value === true;
144− }
150+ },
145− })
151+ });
146
152
147 const esvisible =
153 const esvisible =
148− props.networkType === NETWORK_NAMES.ROPSTEN ||
154+ props.networkType === NETWORK_NAMES.ROPSTEN ||
149− props.networkType === NETWORK_NAMES.MAINNET
155+ props.networkType === NETWORK_NAMES.MAINNET;
150
156
151 const esdomain =
157 const esdomain =
152− props.networkType === NETWORK_NAMES.ROPSTEN
158+ props.networkType === NETWORK_NAMES.ROPSTEN
153− ? "ropsten.etherscan.io"
159+ ? 'ropsten.etherscan.io'
154− : "etherscan.io"
160+ : 'etherscan.io';
155
161
156 return (
162 return (
157 <Row>
163 <Row>
158 <Col>
164 <Col>
159 <H1>
165 <H1>
166+ {'Issue a Child From '}{' '}
160− { 'Issue a Child From ' } <code>{ `${ob.patp(state.issuingPoint)}` }</code>
167+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
161 </H1>
168 </H1>
162
169
163−
164 <P>
170 <P>
165− {
166− `Please enter the point you would like to issue, and specify the
171+ {`Please enter the point you would like to issue, and specify the
167 receiving Ethereum address. If you need to create an address, you
172 receiving Ethereum address. If you need to create an address, you
168− can also use Wallet Generator.`
173+ can also use Wallet Generator.`}
169− }
170 </P>
174 </P>
171
175
172 <P>
176 <P>
173− {
174− `Your point can only issue children with particular names. Some
177+ {`Your point can only issue children with particular names. Some
175− valid suggestions for `
178+ valid suggestions for `}
176− }
177− {
178− <code>
179− { ob.patp(state.issuingPoint) }
179+ {<code>{ob.patp(state.issuingPoint)}</code>}
180− </code>
181− }
182− {
183− ' are '
180+ {' are '}
184− }
185− <code>
186− { state.suggestions[0] }
181+ <code>{state.suggestions[0]}</code>
187− </code>
188− { ', ' }
182+ {', '}
189− <code>
190− { state.suggestions[1] }
183+ <code>{state.suggestions[1]}</code>
191− </code>
192− { ', and ' }
184+ {', and '}
193− <code>
194− { state.suggestions[2] }
185+ <code>{state.suggestions[2]}</code>
195− </code>
196− { '.' }
186+ {'.'}
197 </P>
187 </P>
198
188
199 <PointInput
189 <PointInput
200− prop-size='lg'
190+ prop-size="lg"
201− prop-format='innerLabel'
191+ prop-format="innerLabel"
202 className={'mono mt-8'}
192 className={'mono mt-8'}
203− placeholder={ `e.g. ${state.suggestions[3]}` }
193+ placeholder={`e.g. ${state.suggestions[3]}`}
204− value={ state.desiredPoint }
194+ value={state.desiredPoint}
205− onChange={ this.handlePointInput }>
195+ onChange={this.handlePointInput}>
206− <InnerLabel>{ 'Point to Issue' }</InnerLabel>
196+ <InnerLabel>{'Point to Issue'}</InnerLabel>
207 <div
197 <div
208− style={{marginTop: '8.8rem', marginLeft: '4.6rem', fontSize: '5rem', color: '#757575', whiteSpace: 'pre-wrap'}}
198+ style={{
199+ marginTop: '8.8rem',
200+ marginLeft: '4.6rem',
201+ fontSize: '5rem',
202+ color: '#757575',
203+ whiteSpace: 'pre-wrap',
204+ }}
209− className='abs tl-0 mono'>
205+ className="abs tl-0 mono">
210− { state.autoComplete }
206+ {state.autoComplete}
211 </div>
207 </div>
212 <ValidatedSigil
208 <ValidatedSigil
213 className={'tr-0 mt-05 mr-0 abs'}
209 className={'tr-0 mt-05 mr-0 abs'}
214 patp={state.desiredPoint}
210 patp={state.desiredPoint}
215 size={68}
211 size={68}
216 margin={8}
212 margin={8}
217− validator={() => this.validatePoint(state.desiredPoint)} />
213+ validator={() => this.validatePoint(state.desiredPoint)}
214+ />
218 </PointInput>
215 </PointInput>
219
216
220 <AddressInput
217 <AddressInput
221− className='text-mono mt-8'
218+ className="text-mono mt-8"
222− prop-size='lg'
219+ prop-size="lg"
223− prop-format='innerLabel'
220+ prop-format="innerLabel"
224− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
221+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
225− value={ state.receivingAddress }
222+ value={state.receivingAddress}
223+ disabled={
226− disabled={ Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value }
224+ Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
225+ }
227− onChange={ v => this.handleAddressInput(v) }>
226+ onChange={v => this.handleAddressInput(v)}>
228− <InnerLabel>{ 'Receiving Address' }</InnerLabel>
227+ <InnerLabel>{'Receiving Address'}</InnerLabel>
229 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
228 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
230 </AddressInput>
229 </AddressInput>
231
230
232 <Anchor
231 <Anchor
233 className={'mt-1'}
232 className={'mt-1'}
234 prop-size={'sm'}
233 prop-size={'sm'}
234+ prop-disabled={
235− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
235+ !isValidAddress(state.receivingAddress) || !esvisible
236+ }
236 target={'_blank'}
237 target={'_blank'}
237 href={`https://${esdomain}/address/${state.receivingAddress}`}>
238 href={`https://${esdomain}/address/${state.receivingAddress}`}>
238− {'View on Etherscan ↗'}
239+ {'View on Etherscan ↗'}
239 </Anchor>
240 </Anchor>
240
241
241 <StatelessTransaction
242 <StatelessTransaction
@@ -244,11 +245,12 @@ class IssueChild extends React.Component {
244 // Other
245 // Other
245 canGenerate={canGenerate}
246 canGenerate={canGenerate}
246 createUnsignedTxn={this.createUnsignedTxn}
247 createUnsignedTxn={this.createUnsignedTxn}
247− ref={this.statelessRef} />
248+ ref={this.statelessRef}
249+ />
248 </Col>
250 </Col>
249 </Row>
251 </Row>
250− )
252+ );
251 }
253 }
252}
254}
253
255
254−export default IssueChild
256+export default IssueChild;
src/bridge/views/Keystore.js+104−108
@@ -1,168 +1,164 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import { Ok, Error } from 'folktale/result'
2+import { Ok, Error } from 'folktale/result';
3−import React from 'react'
3+import React from 'react';
4−import * as keythereum from 'keythereum'
4+import * as keythereum from 'keythereum';
5−import { Button, UploadButton } from '../components/Base'
5+import { Button, UploadButton } from '../components/Base';
6−import { Input, InnerLabel, InputCaption } from '../components/Base'
6+import { Input, InnerLabel, InputCaption } from '../components/Base';
7−import { Row, Col, H1, H3, Warning } from '../components/Base'
7+import { Row, Col, H1, H3, Warning } from '../components/Base';
8−
8+
9−import { BRIDGE_ERROR } from '../lib/error'
9+import { BRIDGE_ERROR } from '../lib/error';
10−import { ROUTE_NAMES } from '../lib/router'
10+import { ROUTE_NAMES } from '../lib/router';
11−import { EthereumWallet } from '../lib/wallet'
11+import { EthereumWallet } from '../lib/wallet';
12
12
13class Keystore extends React.Component {
13class Keystore extends React.Component {
14−
15 constructor(props) {
14 constructor(props) {
16− super(props)
15+ super(props);
17
16
18 this.state = {
17 this.state = {
19 keystore: Nothing(), // Maybe<Result<String, String>>
18 keystore: Nothing(), // Maybe<Result<String, String>>
20 password: '',
19 password: '',
21− decryptionProblem: false
20+ decryptionProblem: false,
22− }
21+ };
23
22
24− this.handleKeystoreUpload = this.handleKeystoreUpload.bind(this)
23+ this.handleKeystoreUpload = this.handleKeystoreUpload.bind(this);
25− this.handlePasswordInput = this.handlePasswordInput.bind(this)
24+ this.handlePasswordInput = this.handlePasswordInput.bind(this);
26− this.constructWallet = this.constructWallet.bind(this)
25+ this.constructWallet = this.constructWallet.bind(this);
27 }
26 }
28
27
29 handlePasswordInput(password) {
28 handlePasswordInput(password) {
30− this.setState({ password })
29+ this.setState({ password });
31 }
30 }
32
31
33 constructWallet() {
32 constructWallet() {
34− const { setWallet } = this.props
33+ const { setWallet } = this.props;
35− const { keystore, password } = this.state
34+ const { keystore, password } = this.state;
36
35
37 try {
36 try {
38−
39 const text = keystore.matchWith({
37 const text = keystore.matchWith({
38+ Nothing: _ => {
40− Nothing: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE },
39+ throw BRIDGE_ERROR.MISSING_KEYSTORE;
40+ },
41+ Just: ks =>
41− Just: ks => ks.value.matchWith({
42+ ks.value.matchWith({
42− Ok: result => result.value,
43+ Ok: result => result.value,
44+ Error: _ => {
43− Error: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE }
45+ throw BRIDGE_ERROR.MISSING_KEYSTORE;
46+ },
44− })
47+ }),
45− })
48+ });
46−
49+
47− const json = JSON.parse(text)
50+ const json = JSON.parse(text);
48− const privateKey = keythereum.recover(password, json)
51+ const privateKey = keythereum.recover(password, json);
49−
52+
50− const wallet = new EthereumWallet(privateKey)
53+ const wallet = new EthereumWallet(privateKey);
51− this.setState({ decryptionProblem: false })
54+ this.setState({ decryptionProblem: false });
52− setWallet(Just(wallet))
55+ setWallet(Just(wallet));
53−
54 } catch (err) {
56 } catch (err) {
55−
56− this.setState({ decryptionProblem: true })
57+ this.setState({ decryptionProblem: true });
57− setWallet(Nothing())
58+ setWallet(Nothing());
58−
59 }
59 }
60 }
60 }
61
61
62− handleKeystoreUpload = (event) => {
62+ handleKeystoreUpload = event => {
63− const file = event.files.item(0)
63+ const file = event.files.item(0);
64− const reader = new FileReader()
64+ const reader = new FileReader();
65
65
66− reader.onload = (e) => {
66+ reader.onload = e => {
67− const keystore = e.target.result
67+ const keystore = e.target.result;
68− this.setState({ keystore: Just(Ok(keystore)) })
68+ this.setState({ keystore: Just(Ok(keystore)) });
69− }
69+ };
70
70
71 const failure = _ => {
71 const failure = _ => {
72− const message = 'There was a problem uploading your Keystore file'
72+ const message = 'There was a problem uploading your Keystore file';
73− this.setState({ keystore: Just(Error(message)) })
73+ this.setState({ keystore: Just(Error(message)) });
74− }
74+ };
75
75
76− reader.onerror = failure
76+ reader.onerror = failure;
77− reader.onabort = failure
77+ reader.onabort = failure;
78
78
79− reader.readAsText(file)
79+ reader.readAsText(file);
80− }
80+ };
81
81
82 render() {
82 render() {
83− const { pushRoute, popRoute, wallet } = this.props
83+ const { pushRoute, popRoute, wallet } = this.props;
84− const { keystore, password, decryptionProblem } = this.state
84+ const { keystore, password, decryptionProblem } = this.state;
85
85
86 const uploadButtonClass = keystore.matchWith({
86 const uploadButtonClass = keystore.matchWith({
87 Nothing: _ => 'bg-blue white',
87 Nothing: _ => 'bg-blue white',
88+ Just: ks =>
88− Just: ks => ks.value.matchWith({
89+ ks.value.matchWith({
89− Ok: _ => 'bg-green white',
90+ Ok: _ => 'bg-green white',
90− Error: _ => 'bg-yellow black'
91+ Error: _ => 'bg-yellow black',
91− })
92+ }),
92− })
93+ });
93
94
94 const decryptMessage =
95 const decryptMessage =
95− decryptionProblem === false
96+ decryptionProblem === false ? (
96− ? <div />
97+ <div />
98+ ) : (
97− : <Warning className={'mt-8'}>
99+ <Warning className={'mt-8'}>
98− <H3 style={{marginTop: 0, paddingTop: 0}}>
100+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
99− {
100− "Couldn't decrypt wallet."
101+ {"Couldn't decrypt wallet."}
101− }
102 </H3>
102 </H3>
103− { 'You may have entered an incorrect password.' }
103+ {'You may have entered an incorrect password.'}
104 </Warning>
104 </Warning>
105+ );
105
106
106 return (
107 return (
107− <Row>
108+ <Row>
108− <Col className={'measure-md'}>
109+ <Col className={'measure-md'}>
109− <H1 className={'mb-4'}>{ 'Upload Your Keystore File' }</H1>
110+ <H1 className={'mb-4'}>{'Upload Your Keystore File'}</H1>
110 <InputCaption>
111 <InputCaption>
111− { `Please upload your Ethereum keystore file. If your keystore
112+ {`Please upload your Ethereum keystore file. If your keystore
112 file is encrypted with a password, you'll also need to enter
113 file is encrypted with a password, you'll also need to enter
113− that below.` }
114+ that below.`}
114 </InputCaption>
115 </InputCaption>
115
116
116 <UploadButton
117 <UploadButton
117− className={ `btn ${uploadButtonClass} mt-10` }
118+ className={`btn ${uploadButtonClass} mt-10`}
118− onChange={ this.handleKeystoreUpload }
119+ onChange={this.handleKeystoreUpload}>
119− >
120− <div className={'flex-center-all fs-4 h-11 pointer'}>{ 'Upload Keystore file' }</div>
120+ <div className={'flex-center-all fs-4 h-11 pointer'}>
121+ {'Upload Keystore file'}
122+ </div>
121 </UploadButton>
123 </UploadButton>
122
124
123 <Input
125 <Input
124− className='pt-8 mt-8'
126+ className="pt-8 mt-8"
125− prop-size='md'
127+ prop-size="md"
126− prop-format='innerLabel'
128+ prop-format="innerLabel"
127− type='password'
129+ type="password"
128− name='password'
130+ name="password"
129− onChange={ this.handlePasswordInput }
131+ onChange={this.handlePasswordInput}
130− value={ password }
132+ value={password}
131− autocomplete='off'
133+ autocomplete="off"
132 autoFocus>
134 autoFocus>
133 <InnerLabel>{'Password'}</InnerLabel>
135 <InnerLabel>{'Password'}</InnerLabel>
134 </Input>
136 </Input>
135
137
136 <Button
138 <Button
137 className={` mt-10`}
139 className={` mt-10`}
138− prop-size={ 'wide lg' }
140+ prop-size={'wide lg'}
139− disabled={ Nothing.hasInstance(keystore) }
141+ disabled={Nothing.hasInstance(keystore)}
140− onClick={ this.constructWallet }
142+ onClick={this.constructWallet}>
141− >
142− { 'Decrypt' }
143+ {'Decrypt'}
143 </Button>
144 </Button>
144
145
145− { decryptMessage }
146+ {decryptMessage}
146
147
147 <Button
148 <Button
148 className={'mt-10'}
149 className={'mt-10'}
149− prop-size={ 'wide lg' }
150+ prop-size={'wide lg'}
150− disabled={ Nothing.hasInstance(wallet) }
151+ disabled={Nothing.hasInstance(wallet)}
151− onClick={ () => {
152+ onClick={() => {
152− popRoute()
153+ popRoute();
153− pushRoute(ROUTE_NAMES.SHIPS)
154+ pushRoute(ROUTE_NAMES.SHIPS);
154− }
155− }
155+ }}>
156− >
157− { 'Continue →' }
156+ {'Continue →'}
158 </Button>
157 </Button>
159−
158+ </Col>
160− </Col>
161− </Row>
159+ </Row>
162−
163−
164− )
160+ );
165 }
161 }
166}
162}
167
163
168−export default Keystore
164+export default Keystore;
src/bridge/views/Landing.js+50−60
@@ -1,68 +1,57 @@
1−import React from 'react'
1+import React from 'react';
2−import { Button } from '../components/Base'
2+import { Button } from '../components/Base';
3−import { Row, Col } from '../components/Base'
3+import { Row, Col } from '../components/Base';
4−import { H1, P } from '../components/Base'
4+import { H1, P } from '../components/Base';
5
5
6−import { ROUTE_NAMES } from '../lib/router'
6+import { ROUTE_NAMES } from '../lib/router';
7
7
8−const Landing = (props) =>
8+const Landing = props => (
9− <Row>
9+ <Row>
10− <Col>
10+ <Col>
11− <H1>{ 'Welcome' }</H1>
11+ <H1>{'Welcome'}</H1>
12
12
13+ <P>
14+ {'Bridge is a tool for managing and viewing assets on ' +
15+ 'Azimuth, the Urbit address space.'}
16+ </P>
13
17
14− <P>
18+ <Button
15− { 'Bridge is a tool for managing and viewing assets on ' +
19+ prop-type="link"
16− 'Azimuth, the Urbit address space.' }
20+ prop-size="lg"
17− </P>
21+ className={'mb-4 mt-8'}
22+ onClick={() => props.pushRoute(ROUTE_NAMES.INVITE_TICKET)}>
23+ {'Claim an invite →'}
24+ </Button>
18
25
26+ <P>{'If you were sent an Azimuth invite code, start here.'}</P>
19
27
28+ <Button
29+ prop-type="link"
30+ prop-size="lg"
31+ className={'mb-4 mt-8'}
32+ onClick={() => props.pushRoute(ROUTE_NAMES.NETWORK)}>
33+ {'Unlock a Wallet →'}
34+ </Button>
20
35
21− <Button
22− prop-type='link'
23− prop-size='lg'
24− className={'mb-4 mt-8'}
25− onClick={ () => props.pushRoute(ROUTE_NAMES.INVITE_TICKET) }
26− >
27− { 'Claim an invite →' }
28− </Button>
29−
30− <P>
36+ <P>
31− { "If you were sent an Azimuth invite code, start here." }
32− </P>
33−
34−
35− <Button
36− prop-type='link'
37− prop-size='lg'
38− className={'mb-4 mt-8'}
39− onClick={ () => props.pushRoute(ROUTE_NAMES.NETWORK) }
40− >
41− { 'Unlock a Wallet →' }
42− </Button>
43−
44− <P>
45− { "If you own Azimuth assets and want to manage them in some " +
37+ {'If you own Azimuth assets and want to manage them in some ' +
46 "way, start here. You'll need either your Urbit ticket or a " +
38 "way, start here. You'll need either your Urbit ticket or a " +
47− "keypair." }
39+ 'keypair.'}
48− </P>
40+ </P>
49
41
50− {
42+ {
51− <Row>
43+ <Row>
52− <Col>
44+ <Col>
53− <Button
45+ <Button
54− prop-type={'link'}
46+ prop-type={'link'}
55− prop-size={'lg'}
47+ prop-size={'lg'}
56− className={'mb-4 mt-8'}
48+ className={'mb-4 mt-8'}
57− onClick={ () => props.pushRoute(ROUTE_NAMES.VIEW_SHIP) }
49+ onClick={() => props.pushRoute(ROUTE_NAMES.VIEW_SHIP)}>
58− >
59− { 'View a point →' }
50+ {'View a point →'}
60− </Button>
51+ </Button>
61− <P>
62− { "View an Azimuth point without signing into a wallet." }
52+ <P>{'View an Azimuth point without signing into a wallet.'}</P>
63− </P>
64− </Col>
53+ </Col>
65− </Row>
54+ </Row>
66 // FIXME add address view
55 // FIXME add address view
67 // <LinkButton
56 // <LinkButton
68 // size={'l'}
57 // size={'l'}
@@ -75,8 +64,9 @@ const Landing = (props) =>
75 // <p>
64 // <p>
76 // { "Lorem" }
65 // { "Lorem" }
77 // </p>
66 // </p>
78− }
67+ }
79− </Col>
68+ </Col>
80− </Row>
69+ </Row>
70+);
81
71
82−export default Landing
72+export default Landing;
src/bridge/views/Ledger.js+144−135
@@ -1,38 +1,32 @@
1−import * as bip32 from 'bip32'
1+import * as bip32 from 'bip32';
2−import React from 'react'
2+import React from 'react';
3−import Maybe from 'folktale/maybe'
3+import Maybe from 'folktale/maybe';
4−import { Button } from '../components/Base'
4+import { Button } from '../components/Base';
5−import { Row, Col, H1, P, H2 } from '../components/Base'
5+import { Row, Col, H1, P, H2 } from '../components/Base';
6−import { Input, InnerLabel, InnerLabelDropdown } from '../components/Base'
6+import { Input, InnerLabel, InnerLabelDropdown } from '../components/Base';
7−import Transport from '@ledgerhq/hw-transport-u2f'
7+import Transport from '@ledgerhq/hw-transport-u2f';
8−import Eth from '@ledgerhq/hw-app-eth'
8+import Eth from '@ledgerhq/hw-app-eth';
9−import * as secp256k1 from 'secp256k1'
9+import * as secp256k1 from 'secp256k1';
10−
10+
11−import { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH } from '../lib/ledger'
11+import { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH } from '../lib/ledger';
12−import { ROUTE_NAMES } from '../lib/router'
12+import { ROUTE_NAMES } from '../lib/router';
13−
13+
14−const chopHdPrefix = str =>
14+const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
15− str.slice(0, 2) === "m/"
15+
16− ? str.slice(2)
17− : str
18−
19−const addHdPrefix = str =>
16+const addHdPrefix = str => (str.slice(0, 2) === 'm/' ? str : 'm/' + str);
20− str.slice(0, 2) === "m/"
21− ? str
22− : "m/" + str
23
17
24class Ledger extends React.Component {
18class Ledger extends React.Component {
25 constructor(props) {
19 constructor(props) {
26− super(props)
20+ super(props);
27
21
28 this.state = {
22 this.state = {
29 basePath: LEDGER_LIVE_PATH,
23 basePath: LEDGER_LIVE_PATH,
30 hdpath: LEDGER_LIVE_PATH.replace(/x/g, 0),
24 hdpath: LEDGER_LIVE_PATH.replace(/x/g, 0),
31− account: 0
25+ account: 0,
32− }
26+ };
33
27
34− this.pollDevice = this.pollDevice.bind(this)
28+ this.pollDevice = this.pollDevice.bind(this);
35− this.handleHdPathInput = this.handleHdPathInput.bind(this)
29+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
36 this.handlePathSelection = this.handlePathSelection.bind(this);
30 this.handlePathSelection = this.handlePathSelection.bind(this);
37 this.handleAccountSelection = this.handleAccountSelection.bind(this);
31 this.handleAccountSelection = this.handleAccountSelection.bind(this);
38 this.updateHdPath = this.updateHdPath.bind(this);
32 this.updateHdPath = this.updateHdPath.bind(this);
@@ -55,157 +49,172 @@ class Ledger extends React.Component {
55 }
49 }
56
50
57 handleHdPathInput(hdpath) {
51 handleHdPathInput(hdpath) {
58− this.setState({ hdpath })
52+ this.setState({ hdpath });
59 }
53 }
60
54
61 async pollDevice() {
55 async pollDevice() {
62− const { setWallet, setWalletHdPath } = this.props
56+ const { setWallet, setWalletHdPath } = this.props;
63− const { hdpath } = this.state
57+ const { hdpath } = this.state;
64−
58+
65− const transport = await Transport.create()
59+ const transport = await Transport.create();
66− const eth = new Eth(transport)
60+ const eth = new Eth(transport);
67− const path = chopHdPrefix(hdpath)
61+ const path = chopHdPrefix(hdpath);
68−
62+
69− eth.getAddress(path, false, true)
63+ eth.getAddress(path, false, true).then(
70− .then(info => {
64+ info => {
71− const publicKey = Buffer.from(info.publicKey, 'hex')
65+ const publicKey = Buffer.from(info.publicKey, 'hex');
72− const chainCode = Buffer.from(info.chainCode, 'hex')
66+ const chainCode = Buffer.from(info.chainCode, 'hex');
73− const pub = secp256k1.publicKeyConvert(publicKey, true)
67+ const pub = secp256k1.publicKeyConvert(publicKey, true);
74− const hd = bip32.fromPublicKey(pub, chainCode)
68+ const hd = bip32.fromPublicKey(pub, chainCode);
75− setWallet(Maybe.Just(hd))
69+ setWallet(Maybe.Just(hd));
76− setWalletHdPath(addHdPrefix(hdpath))
70+ setWalletHdPath(addHdPrefix(hdpath));
71+ },
77− }, _ => {
72+ _ => {
78− setWallet(Maybe.Nothing())
73+ setWallet(Maybe.Nothing());
79− })
74+ }
75+ );
80 }
76 }
81
77
82 render() {
78 render() {
83− const { pushRoute, popRoute, wallet } = this.props
79+ const { pushRoute, popRoute, wallet } = this.props;
84 const { basePath, hdpath, account } = this.state;
80 const { basePath, hdpath, account } = this.state;
85 const { handlePathSelection, handleAccountSelection } = this;
81 const { handlePathSelection, handleAccountSelection } = this;
86
82
87 const pathOptions = [
83 const pathOptions = [
88− { title: 'Ledger Live',
89− value: LEDGER_LIVE_PATH
84+ { title: 'Ledger Live', value: LEDGER_LIVE_PATH },
90− },
91− { title: 'Ledger Legacy',
92− value: LEDGER_LEGACY_PATH
85+ { title: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
93− },
94 {
86 {
95 title: 'Custom path',
87 title: 'Custom path',
96− value: 'custom'
88+ value: 'custom',
97− }
89+ },
98 ];
90 ];
99− let basePathTitle = pathOptions.find(o => (o.value === basePath)).title;
91+ let basePathTitle = pathOptions.find(o => o.value === basePath).title;
100
92
101 let accountOptions = [];
93 let accountOptions = [];
102 for (let i = 0; i < 20; i++) {
94 for (let i = 0; i < 20; i++) {
103 accountOptions.push({
95 accountOptions.push({
104− title: 'Account #' + (i+1),
96+ title: 'Account #' + (i + 1),
105− value: i
97+ value: i,
106 });
98 });
107 }
99 }
108− let accountTitle = accountOptions.find(o => (o.value === account)).title;
100+ let accountTitle = accountOptions.find(o => o.value === account).title;
109
101
110 const basePathSelection = (
102 const basePathSelection = (
111 <InnerLabelDropdown
103 <InnerLabelDropdown
112− className='mt-8'
104+ className="mt-8"
113 options={pathOptions}
105 options={pathOptions}
114 handleUpdate={handlePathSelection}
106 handleUpdate={handlePathSelection}
115 title={'Derivation path'}
107 title={'Derivation path'}
116 currentSelectionTitle={basePathTitle}
108 currentSelectionTitle={basePathTitle}
117− fullWidth={true}
118− >
119− </InnerLabelDropdown>
109+ fullWidth={true}></InnerLabelDropdown>
120 );
110 );
121
111
122− const truePathSelection = (basePath === 'custom')
112+ const truePathSelection =
113+ basePath === 'custom' ? (
123− ? ( <Input
114+ <Input
124− className='pt-8 mt-4 text-mono'
115+ className="pt-8 mt-4 text-mono"
125− prop-size='md'
116+ prop-size="md"
126− prop-format='innerLabel'
117+ prop-format="innerLabel"
127− name='hdpath'
118+ name="hdpath"
128− value={ addHdPrefix(hdpath) }
119+ value={addHdPrefix(hdpath)}
129− autocomplete='off'
120+ autocomplete="off"
130− onChange={ this.handleHdPathInput }>
121+ onChange={this.handleHdPathInput}>
131− <InnerLabel>{'HD Path'}</InnerLabel>
122+ <InnerLabel>{'HD Path'}</InnerLabel>
132− </Input> )
123+ </Input>
124+ ) : (
133− : ( <InnerLabelDropdown
125+ <InnerLabelDropdown
134− className='mt-4'
126+ className="mt-4"
135− prop-size='md'
127+ prop-size="md"
136− prop-format='innerLabel'
128+ prop-format="innerLabel"
137− options={accountOptions}
129+ options={accountOptions}
138− handleUpdate={handleAccountSelection}
130+ handleUpdate={handleAccountSelection}
139− title={'Account'}
131+ title={'Account'}
140− currentSelectionTitle={accountTitle}
132+ currentSelectionTitle={accountTitle}
141− fullWidth={true}
133+ fullWidth={true}></InnerLabelDropdown>
142− >
134+ );
143− </InnerLabelDropdown> );
144
135
145 return (
136 return (
137+ <Row>
138+ <Col className={'measure-md'}>
139+ <H1>{'Authenticate With Your Ledger'}</H1>
146
140
147− <Row>
141+ <H2>{'Running on HTTPS?'}</H2>
148− <Col className={'measure-md'}>
149− <H1>{ 'Authenticate With Your Ledger' }</H1>
150
142
151− <H2>{'Running on HTTPS?'}</H2>
152−
153− <P>
143+ <P>
154− { `Connect and authenticate to your Ledger, and then open the
144+ {`Connect and authenticate to your Ledger, and then open the
155 "Ethereum" application. If you're running on older firmware,
145 "Ethereum" application. If you're running on older firmware,
156 make sure the "browser support" option is turned on. To sign
146 make sure the "browser support" option is turned on. To sign
157 transactions, you'll also need to enable the "contract data"
147 transactions, you'll also need to enable the "contract data"
158− option.` }
148+ option.`}
159− </P>
149+ </P>
160
150
161− <P>
151+ <P>
162− { `If you'd like to use a custom derivation path, you may enter
152+ {`If you'd like to use a custom derivation path, you may enter
163− it below.`
153+ it below.`}
164− }
165− </P>
154+ </P>
166
155
167− <H2>{'Running on HTTP?'}</H2>
156+ <H2>{'Running on HTTP?'}</H2>
168
157
169− <P>{`To authenticate and sign transactions with a Ledger, Bridge
158+ <P>{`To authenticate and sign transactions with a Ledger, Bridge
170 must be serving over HTTPS on localhost. You can do this via the
159 must be serving over HTTPS on localhost. You can do this via the
171 following:`}</P>
160 following:`}</P>
172
161
173− <ol className={'measure-md'}>
162+ <ol className={'measure-md'}>
163+ <li className={'mt-4'}>
164+ {'Install'}{' '}
165+ <a
166+ target={'_blank'}
174− <li className={'mt-4'}>{'Install'} <a target={'_blank'} href={'https://github.com/FiloSottile/mkcert'}>{'mkcert'}</a></li>
167+ href={'https://github.com/FiloSottile/mkcert'}>
168+ {'mkcert'}
169+ </a>
170+ </li>
171+ <li className={'mt-4'}>
175− <li className={'mt-4'}>{'Install a local certificate authority via '}<code>{'mkcert -install'}</code></li>
172+ {'Install a local certificate authority via '}
173+ <code>{'mkcert -install'}</code>
174+ </li>
175+ <li className={'mt-4'}>
176+ {'In your '}
177+ <code>{'bridge'}</code>
178+ {
176− <li className={'mt-4'}>{'In your '}<code>{'bridge'}</code>{' directory, generate a certificate valid for localhost via '} <code>{'mkcert localhost'}</code>{'.'}
179+ ' directory, generate a certificate valid for localhost via '
180+ }{' '}
181+ <code>{'mkcert localhost'}</code>
182+ {'.'}
183+ {'This will produce two files: '}
177− {'This will produce two files: '}<code>{'localhost.pem'}</code> {', the local certificate, and '}<code>{'localhost-key.pem'}</code>{', its corresponding private key.'}
184+ <code>{'localhost.pem'}</code> {', the local certificate, and '}
185+ <code>{'localhost-key.pem'}</code>
186+ {', its corresponding private key.'}
178− </li>
187+ </li>
188+ <li className={'mt-4'}>
189+ {'Run '}
179− <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
190+ <code>{'python bridge-https.py'}</code>
191+ </li>
180− </ol>
192+ </ol>
181−
193+
182− { basePathSelection }
194+ {basePathSelection}
183− { truePathSelection }
195+ {truePathSelection}
184−
196+
185− <Button
197+ <Button
186− prop-size={'wide lg'}
198+ prop-size={'wide lg'}
187− className={'mt-8'}
199+ className={'mt-8'}
188− onClick={ this.pollDevice }>
200+ onClick={this.pollDevice}>
189− { 'Authenticate →' }
201+ {'Authenticate →'}
190− </Button>
202+ </Button>
191−
203+
192− <Button
204+ <Button
193− className={'mt-8'}
205+ className={'mt-8'}
194− prop-size={'wide lg'}
206+ prop-size={'wide lg'}
195− disabled={ Maybe.Nothing.hasInstance(wallet) }
207+ disabled={Maybe.Nothing.hasInstance(wallet)}
196− onClick={
208+ onClick={() => {
197− () => {
198− popRoute()
209+ popRoute();
199− pushRoute(ROUTE_NAMES.SHIPS)
210+ pushRoute(ROUTE_NAMES.SHIPS);
200− }
201− }>
211+ }}>
202− { 'Continue →' }
212+ {'Continue →'}
203− </Button>
213+ </Button>
204−
205 </Col>
214 </Col>
206 </Row>
215 </Row>
207− )
216+ );
208 }
217 }
209}
218}
210
219
211−export default Ledger
220+export default Ledger;
src/bridge/views/Mnemonic.js+105−116
@@ -1,176 +1,165 @@
1−import * as bip39 from 'bip39'
1+import * as bip39 from 'bip39';
2−import { Just, Nothing } from 'folktale/maybe'
2+import { Just, Nothing } from 'folktale/maybe';
3−import React from 'react'
3+import React from 'react';
4−import { Button } from '../components/Base'
4+import { Button } from '../components/Base';
5import {
5import {
6 Input,
6 Input,
7 MnemonicInput,
7 MnemonicInput,
8 InnerLabel,
8 InnerLabel,
9− InputCaption
9+ InputCaption,
10− } from '../components/Base'
10+} from '../components/Base';
11−import { Row, Col, H1 } from '../components/Base'
11+import { Row, Col, H1 } from '../components/Base';
12−
13−import { ROUTE_NAMES } from '../lib/router'
14−import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet'
15
12
13+import { ROUTE_NAMES } from '../lib/router';
14+import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet';
16
15
17class Mnemonic extends React.Component {
16class Mnemonic extends React.Component {
18−
19 constructor(props) {
17 constructor(props) {
20− super(props)
18+ super(props);
21
19
22 this.state = {
20 this.state = {
23 mnemonic: '',
21 mnemonic: '',
24 passphrase: '',
22 passphrase: '',
25 hdpath: DEFAULT_HD_PATH,
23 hdpath: DEFAULT_HD_PATH,
26− }
24+ };
27
25
28− this.handleMnemonicInput = this.handleMnemonicInput.bind(this)
26+ this.handleMnemonicInput = this.handleMnemonicInput.bind(this);
29− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
27+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
30− this.handleHdPathInput = this.handleHdPathInput.bind(this)
28+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
31 }
29 }
32
30
33 componentDidMount() {
31 componentDidMount() {
34− const { mnemonic, hdpath, passphrase } = this.state
32+ const { mnemonic, hdpath, passphrase } = this.state;
35− this.attemptWalletDerivation(mnemonic, hdpath, passphrase)
33+ this.attemptWalletDerivation(mnemonic, hdpath, passphrase);
36
34
37 this.setState({
35 this.setState({
38 exampleMnemonic: bip39.generateMnemonic(),
36 exampleMnemonic: bip39.generateMnemonic(),
39− })
37+ });
40 }
38 }
41
39
42 handlePassphraseInput(passphrase) {
40 handlePassphraseInput(passphrase) {
43 this.setState((state, _) => {
41 this.setState((state, _) => {
44− const mnemonic = state.mnemonic
42+ const mnemonic = state.mnemonic;
45− const hdpath = state.hdpath
43+ const hdpath = state.hdpath;
46 this.attemptWalletDerivation(
44 this.attemptWalletDerivation(
47 mnemonic,
45 mnemonic,
48 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
46 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
49 passphrase
47 passphrase
50− )
48+ );
51 return {
49 return {
52− passphrase
50+ passphrase,
53− }
51+ };
54− })
52+ });
55 }
53 }
56
54
57 handleMnemonicInput(mnemonic) {
55 handleMnemonicInput(mnemonic) {
58 this.setState((state, _) => {
56 this.setState((state, _) => {
59− const hdpath = state.hdpath
57+ const hdpath = state.hdpath;
60− const passphrase = state.passphrase
58+ const passphrase = state.passphrase;
61 this.attemptWalletDerivation(
59 this.attemptWalletDerivation(
62 mnemonic,
60 mnemonic,
63 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
61 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
64 passphrase
62 passphrase
65− )
63+ );
66 return {
64 return {
67− mnemonic
65+ mnemonic,
68− }
66+ };
69− })
67+ });
70 }
68 }
71
69
72 handleHdPathInput(hdpath) {
70 handleHdPathInput(hdpath) {
73 this.setState((state, _) => {
71 this.setState((state, _) => {
74− const mnemonic = state.mnemonic
72+ const mnemonic = state.mnemonic;
75− const passphrase = state.passphrase
73+ const passphrase = state.passphrase;
76 this.attemptWalletDerivation(
74 this.attemptWalletDerivation(
77 mnemonic,
75 mnemonic,
78 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
76 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
79 passphrase
77 passphrase
80− )
78+ );
81 return {
79 return {
82− hdpath
80+ hdpath,
83− }
81+ };
84− })
82+ });
85 }
83 }
86
84
87 attemptWalletDerivation(mnemonic, hdpath, passphrase) {
85 attemptWalletDerivation(mnemonic, hdpath, passphrase) {
88− const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props
86+ const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props;
89− const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase)
87+ const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase);
90− setWallet(wallet)
88+ setWallet(wallet);
91− setAuthMnemonic(Just(mnemonic))
89+ setAuthMnemonic(Just(mnemonic));
92− setWalletHdPath(hdpath)
90+ setWalletHdPath(hdpath);
93 }
91 }
94
92
95 render() {
93 render() {
96− const { pushRoute, popRoute, wallet } = this.props
94+ const { pushRoute, popRoute, wallet } = this.props;
97− const {
98− mnemonic,
99− hdpath,
100− exampleMnemonic,
95+ const { mnemonic, hdpath, exampleMnemonic, passphrase } = this.state;
101− passphrase
102− } = this.state
103
96
104 return (
97 return (
105− <Row>
98+ <Row>
106− <Col>
99+ <Col>
107− <H1 className={'mb-4'}>{ 'Enter Your Mnemonic' }</H1>
100+ <H1 className={'mb-4'}>{'Enter Your Mnemonic'}</H1>
108− <InputCaption>
101+ <InputCaption>
109− { "Please enter your BIP39 mnemonic here." }
102+ {'Please enter your BIP39 mnemonic here.'}
110− </InputCaption>
103+ </InputCaption>
111−
104+
112− <MnemonicInput
105+ <MnemonicInput
113− className='pt-8'
106+ className="pt-8"
114− prop-size='md'
107+ prop-size="md"
115− prop-format='innerLabel'
108+ prop-format="innerLabel"
116− type='text'
109+ type="text"
117− name='mnemonic'
110+ name="mnemonic"
118− placeholder={ `e.g. ${exampleMnemonic}` }
111+ placeholder={`e.g. ${exampleMnemonic}`}
119− onChange={ this.handleMnemonicInput }
112+ onChange={this.handleMnemonicInput}
120− value={ mnemonic }
113+ value={mnemonic}
121− autocomplete='off'
114+ autocomplete="off"
122− autoFocus>
115+ autoFocus>
123− <InnerLabel>{'Mnemonic'}</InnerLabel>
116+ <InnerLabel>{'Mnemonic'}</InnerLabel>
124− </MnemonicInput>
117+ </MnemonicInput>
125−
118+
126− <InputCaption>
119+ <InputCaption>
127 {`If your wallet requires a passphrase, you may enter it below.`}
120 {`If your wallet requires a passphrase, you may enter it below.`}
128− </InputCaption>
121+ </InputCaption>
129−
122+
130− <Input
123+ <Input
131− className='pt-8'
124+ className="pt-8"
132− prop-size='md'
125+ prop-size="md"
133− prop-format='innerLabel'
126+ prop-format="innerLabel"
134− name='passphrase'
127+ name="passphrase"
135− type='password'
128+ type="password"
136− value={ passphrase }
129+ value={passphrase}
137− autocomplete='off'
130+ autocomplete="off"
138− onChange={ this.handlePassphraseInput }>
131+ onChange={this.handlePassphraseInput}>
139− <InnerLabel>{'Passphrase'}</InnerLabel>
132+ <InnerLabel>{'Passphrase'}</InnerLabel>
140− </Input>
133+ </Input>
141−
134+
142− <InputCaption>
135+ <InputCaption>
143 {`If you'd like to use a custom derivation path, you may enter it below.`}
136 {`If you'd like to use a custom derivation path, you may enter it below.`}
144− </InputCaption>
137+ </InputCaption>
145−
138+
146− <Input
139+ <Input
147− className='pt-8 text-mono'
140+ className="pt-8 text-mono"
148− prop-size='md'
141+ prop-size="md"
149− prop-format='innerLabel'
142+ prop-format="innerLabel"
150− name='hdpath'
143+ name="hdpath"
151− value={ hdpath }
144+ value={hdpath}
152− autocomplete='off'
145+ autocomplete="off"
153− onChange={ this.handleHdPathInput }>
146+ onChange={this.handleHdPathInput}>
154− <InnerLabel>{'HD Path'}</InnerLabel>
147+ <InnerLabel>{'HD Path'}</InnerLabel>
155− </Input>
148+ </Input>
156−
149+
157− <Button
150+ <Button
158− className={'mt-10'}
151+ className={'mt-10'}
159− disabled={ Nothing.hasInstance(wallet) }
152+ disabled={Nothing.hasInstance(wallet)}
160− onClick={ () => {
153+ onClick={() => {
161− popRoute()
154+ popRoute();
162− pushRoute(ROUTE_NAMES.SHIPS)
155+ pushRoute(ROUTE_NAMES.SHIPS);
163− }
156+ }}>
164− }
165− >
166− { 'Continue →' }
157+ {'Continue →'}
167− </Button>
158+ </Button>
168− </Col>
159+ </Col>
169− </Row>
160+ </Row>
170−
171−
172− )
161+ );
173 }
162 }
174}
163}
175
164
176−export default Mnemonic
165+export default Mnemonic;
src/bridge/views/Network.js+90−90
@@ -1,65 +1,62 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Button } from '../components/Base'
3+import { Button } from '../components/Base';
4−import { InnerLabelDropdown } from '../components/Base'
4+import { InnerLabelDropdown } from '../components/Base';
5−import { Row, Col, H1, P } from '../components/Base'
5+import { Row, Col, H1, P } from '../components/Base';
6−import * as azimuth from 'azimuth-js'
6+import * as azimuth from 'azimuth-js';
7−import Web3 from 'web3'
7+import Web3 from 'web3';
8−
8+
9−import { CONTRACT_ADDRESSES } from '../lib/contracts'
9+import { CONTRACT_ADDRESSES } from '../lib/contracts';
10−import { NETWORK_NAMES, renderNetworkType } from '../lib/network'
10+import { NETWORK_NAMES, renderNetworkType } from '../lib/network';
11−import { ROUTE_NAMES } from '../lib/router'
11+import { ROUTE_NAMES } from '../lib/router';
12
12
13class Network extends React.Component {
13class Network extends React.Component {
14−
15 constructor(props) {
14 constructor(props) {
16− super(props)
15+ super(props);
17
16
18 this.handleNetworkChange = this.handleNetworkChange.bind(this);
17 this.handleNetworkChange = this.handleNetworkChange.bind(this);
19 }
18 }
20
19
21 handleNetworkChange(network) {
20 handleNetworkChange(network) {
22− const { setNetworkType, setNetwork } = this.props
21+ const { setNetworkType, setNetwork } = this.props;
23
22
24 if (network === NETWORK_NAMES.LOCAL) {
23 if (network === NETWORK_NAMES.LOCAL) {
25− setNetworkType(network)
24+ setNetworkType(network);
26
25
27− const endpoint = 'ws://localhost:8545'
26+ const endpoint = 'ws://localhost:8545';
28− const provider = new Web3.providers.WebsocketProvider(endpoint)
27+ const provider = new Web3.providers.WebsocketProvider(endpoint);
29− const web3 = new Web3(provider)
28+ const web3 = new Web3(provider);
30− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
29+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
31
30
32− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
31+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
33 }
32 }
34
33
35 if (network === NETWORK_NAMES.ROPSTEN) {
34 if (network === NETWORK_NAMES.ROPSTEN) {
36− setNetworkType(network)
35+ setNetworkType(network);
37
36
38− const endpoint =
39− `https://ropsten.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
37+ const endpoint = `https://ropsten.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
40
38
41− const provider = new Web3.providers.HttpProvider(endpoint)
39+ const provider = new Web3.providers.HttpProvider(endpoint);
42− const web3 = new Web3(provider)
40+ const web3 = new Web3(provider);
43− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.ROPSTEN)
41+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.ROPSTEN);
44
42
45− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
43+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
46 }
44 }
47
45
48 if (network === NETWORK_NAMES.MAINNET) {
46 if (network === NETWORK_NAMES.MAINNET) {
49− setNetworkType(network)
47+ setNetworkType(network);
50
48
51− const endpoint =
52− `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
49+ const endpoint = `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
53
50
54− const provider = new Web3.providers.HttpProvider(endpoint)
51+ const provider = new Web3.providers.HttpProvider(endpoint);
55− const web3 = new Web3(provider)
52+ const web3 = new Web3(provider);
56− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET)
53+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
57
54
58− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
55+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
59 }
56 }
60
57
61 if (network === NETWORK_NAMES.OFFLINE) {
58 if (network === NETWORK_NAMES.OFFLINE) {
62− setNetworkType(network)
59+ setNetworkType(network);
63
60
64 // NB (jtobin):
61 // NB (jtobin):
65 //
62 //
@@ -72,74 +69,77 @@ class Network extends React.Component {
72
69
73 // Note: localhost:3456 doesn't actually point to anything, we just need
70 // Note: localhost:3456 doesn't actually point to anything, we just need
74 // a provider to initialize the Web3 object
71 // a provider to initialize the Web3 object
75− const provider = new Web3.providers.HttpProvider("http://localhost:3456")
72+ const provider = new Web3.providers.HttpProvider('http://localhost:3456');
76− const web3 = new Web3(provider)
73+ const web3 = new Web3(provider);
77
74
78 const target =
75 const target =
79− process.env.NODE_ENV === 'development'
76+ process.env.NODE_ENV === 'development'
80− ? CONTRACT_ADDRESSES.DEV
77+ ? CONTRACT_ADDRESSES.DEV
81− : CONTRACT_ADDRESSES.MAINNET
78+ : CONTRACT_ADDRESSES.MAINNET;
82
79
83− const contracts = azimuth.initContracts(web3, target)
80+ const contracts = azimuth.initContracts(web3, target);
84
81
85− setNetwork(Maybe.Nothing(), Maybe.Just(contracts))
82+ setNetwork(Maybe.Nothing(), Maybe.Just(contracts));
86 }
83 }
87 }
84 }
88
85
89 getNetworkOptions() {
86 getNetworkOptions() {
90− return [{
87+ return [
88+ {
91− title: 'Main Network (default)',
89+ title: 'Main Network (default)',
92− value: NETWORK_NAMES.MAINNET
90+ value: NETWORK_NAMES.MAINNET,
93− }, {
91+ },
92+ {
94− title: 'Local Node',
93+ title: 'Local Node',
95− value: NETWORK_NAMES.LOCAL
94+ value: NETWORK_NAMES.LOCAL,
96− }, {
95+ },
96+ {
97− title: 'Ropsten',
97+ title: 'Ropsten',
98− value: NETWORK_NAMES.ROPSTEN
98+ value: NETWORK_NAMES.ROPSTEN,
99− }, {
99+ },
100+ {
100− type: 'divider'
101+ type: 'divider',
101− }, {
102+ },
103+ {
102− title: 'Offline',
104+ title: 'Offline',
103− value: NETWORK_NAMES.OFFLINE
105+ value: NETWORK_NAMES.OFFLINE,
106+ },
104− }]
107+ ];
105 }
108 }
106
109
107 render() {
110 render() {
108− const { networkType, pushRoute } = this.props
111+ const { networkType, pushRoute } = this.props;
109− const networkOptions = this.getNetworkOptions()
112+ const networkOptions = this.getNetworkOptions();
110
113
111 return (
114 return (
112− <Row>
115+ <Row>
113− <Col>
116+ <Col>
114− <H1>{ 'Select Network' }</H1>
117+ <H1>{'Select Network'}</H1>
115−
118+
116− <P>
119+ <P>
117− {
118− "Please select the Ethereum node you'd like to send " +
120+ {"Please select the Ethereum node you'd like to send " +
119− "transactions to. For highly valuable keys, please select " +
121+ 'transactions to. For highly valuable keys, please select ' +
120− "offline mode."
122+ 'offline mode.'}
121− }
122− </P>
123+ </P>
123−
124+
124− <InnerLabelDropdown
125+ <InnerLabelDropdown
125− className={'mt-6'}
126+ className={'mt-6'}
126− title={'Node:'}
127+ title={'Node:'}
127− handleUpdate={this.handleNetworkChange}
128+ handleUpdate={this.handleNetworkChange}
128− options={networkOptions}
129+ options={networkOptions}
129− currentSelectionTitle={renderNetworkType(networkType)}>
130+ currentSelectionTitle={renderNetworkType(
131+ networkType
130− </InnerLabelDropdown>
132+ )}></InnerLabelDropdown>
131−
133+
132− <Button
134+ <Button
133− className={'mt-10'}
135+ className={'mt-10'}
134− onClick={ () => pushRoute(ROUTE_NAMES.WALLET) }
136+ onClick={() => pushRoute(ROUTE_NAMES.WALLET)}>
135− >
136− { 'Continue →' }
137+ {'Continue →'}
137− </Button>
138+ </Button>
138−
139− </Col>
139+ </Col>
140− </Row>
140+ </Row>
141− )
141+ );
142 }
142 }
143}
143}
144
144
145−export default Network
145+export default Network;
src/bridge/views/Point.js+85−87
@@ -1,30 +1,28 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { pour } from 'sigil-js'
3+import { pour } from 'sigil-js';
4−import * as ob from 'urbit-ob'
4+import * as ob from 'urbit-ob';
5−import * as azimuth from 'azimuth-js'
5+import * as azimuth from 'azimuth-js';
6−import * as need from '../lib/need'
6+import * as need from '../lib/need';
7−
7+
8−import PointList from '../components/PointList'
8+import PointList from '../components/PointList';
9−import ReactSVGComponents from '../components/ReactSVGComponents'
9+import ReactSVGComponents from '../components/ReactSVGComponents';
10−import KeysAndMetadata from './Point/KeysAndMetadata'
10+import KeysAndMetadata from './Point/KeysAndMetadata';
11−import Actions from './Point/Actions'
11+import Actions from './Point/Actions';
12−import { Row, Col, H1, H3 } from '../components/Base'
12+import { Row, Col, H1, H3 } from '../components/Base';
13−
14
13
15class Point extends React.Component {
14class Point extends React.Component {
16−
17 constructor(props) {
15 constructor(props) {
18− super(props)
16+ super(props);
19
17
20 this.state = {
18 this.state = {
21 spawned: [],
19 spawned: [],
22− invites: Nothing()
20+ invites: Nothing(),
23− }
21+ };
24 }
22 }
25
23
26 componentDidMount() {
24 componentDidMount() {
27− this.cachePoints()
25+ this.cachePoints();
28 }
26 }
29
27
30 componentDidUpdate(prevProps) {
28 componentDidUpdate(prevProps) {
@@ -35,118 +33,118 @@ class Point extends React.Component {
35 // actually leave the Point component, and componentDidMount will not fire
33 // actually leave the Point component, and componentDidMount will not fire
36 // again. But, we can force a call to cachePoints here.
34 // again. But, we can force a call to cachePoints here.
37 //
35 //
38− const oldCursor = prevProps.pointCursor.getOrElse(true)
36+ const oldCursor = prevProps.pointCursor.getOrElse(true);
39− const newCursor = this.props.pointCursor.getOrElse(true)
37+ const newCursor = this.props.pointCursor.getOrElse(true);
40
38
41 if (oldCursor !== newCursor) {
39 if (oldCursor !== newCursor) {
42− this.cachePoints()
40+ this.cachePoints();
43 }
41 }
44 }
42 }
45
43
46 cachePoints() {
44 cachePoints() {
47− const { web3, contracts, pointCursor, addToPointCache } = this.props
45+ const { web3, contracts, pointCursor, addToPointCache } = this.props;
48
46
49 web3.chain(_ =>
47 web3.chain(_ =>
50− contracts.chain(ctrcs =>
48+ contracts.chain(ctrcs =>
51− pointCursor.chain(point => {
49+ pointCursor.chain(point => {
50+ azimuth.azimuth
52− azimuth.azimuth.getPoint(ctrcs, point)
51+ .getPoint(ctrcs, point)
53− .then(details => addToPointCache({ [point]: details }));
52+ .then(details => addToPointCache({ [point]: details }));
53+ azimuth.delegatedSending
54− azimuth.delegatedSending.getTotalUsableInvites(ctrcs, point)
54+ .getTotalUsableInvites(ctrcs, point)
55− .then(count => this.setState({ invites: Just(count) }));
55+ .then(count => this.setState({ invites: Just(count) }));
56− this.updateSpawned(ctrcs, point);
56+ this.updateSpawned(ctrcs, point);
57− })));
57+ })
58+ )
59+ );
58 }
60 }
59
61
60 updateSpawned = contracts => {
62 updateSpawned = contracts => {
61− const { pointCursor } = this.props
63+ const { pointCursor } = this.props;
62
64
63 pointCursor.matchWith({
65 pointCursor.matchWith({
64 Nothing: _ => {},
66 Nothing: _ => {},
65 Just: point => {
67 Just: point => {
68+ azimuth.azimuth
66− azimuth.azimuth.getSpawned(contracts, point.value)
69+ .getSpawned(contracts, point.value)
67− .then(spawned => this.setState({ spawned }))
70+ .then(spawned => this.setState({ spawned }));
68− }
71+ },
69− })
72+ });
70−
71− }
73+ };
72
74
73 render() {
75 render() {
74−
75 const {
76 const {
77+ web3,
78+ popRoute,
76− web3, popRoute, pushRoute, wallet,
79+ pushRoute,
80+ wallet,
77− setPointCursor, pointCache
81+ setPointCursor,
82+ pointCache,
78 } = this.props;
83 } = this.props;
79
84
80− const { spawned } = this.state
85+ const { spawned } = this.state;
81
86
82 const point = need.pointCursor(this.props);
87 const point = need.pointCursor(this.props);
83
88
84 const pointDetails =
89 const pointDetails =
85− point in pointCache
86− ? Just(pointCache[point])
90+ point in pointCache ? Just(pointCache[point]) : Nothing();
87− : Nothing()
88
91
89− const name = ob.patp(point)
92+ const name = ob.patp(point);
90
93
91 const sigil = pour({
94 const sigil = pour({
92 patp: name,
95 patp: name,
93 renderer: ReactSVGComponents,
96 renderer: ReactSVGComponents,
94− size: 256
97+ size: 256,
95− })
98+ });
96
99
97− const online = Just.hasInstance(web3)
100+ const online = Just.hasInstance(web3);
98
101
99− const authenticated = Just.hasInstance(wallet)
102+ const authenticated = Just.hasInstance(wallet);
100
103
101 const routeHandler = route => {
104 const routeHandler = route => {
102− popRoute()
105+ popRoute();
103− pushRoute(route)
106+ pushRoute(route);
104− }
107+ };
105
108
106 const issuedPointList =
109 const issuedPointList =
107− spawned.length === 0
110+ spawned.length === 0 ? (
108− ? <div />
111+ <div />
112+ ) : (
109− : <div>
113+ <div>
110− <H3>{ 'Issued Points' }</H3>
114+ <H3>{'Issued Points'}</H3>
111
115
112 <PointList
116 <PointList
113− setPointCursor={ setPointCursor }
117+ setPointCursor={setPointCursor}
114− routeHandler={ routeHandler }
118+ routeHandler={routeHandler}
115− points={ spawned }
119+ points={spawned}
116 />
120 />
117 </div>
121 </div>
118−
122+ );
119−
120
123
121 return (
124 return (
122− <Row>
125+ <Row>
123− <Col>
126+ <Col>
124− <div className={'mt-12 pt-6'}>
127+ <div className={'mt-12 pt-6'}>{sigil}</div>
125− { sigil }
128+ <H1>
126− </div>
127− <H1><code>{ name }</code></H1>
129+ <code>{name}</code>
128− {
130+ </H1>
129− authenticated
131+ {authenticated ? (
130− ? <Actions
132+ <Actions
131− pushRoute={ pushRoute }
133+ pushRoute={pushRoute}
132− online={ online }
134+ online={online}
133− wallet={ wallet }
135+ wallet={wallet}
134− point={ point }
136+ point={point}
135− pointDetails={ pointDetails }
137+ pointDetails={pointDetails}
136− invites={ this.state.invites } />
138+ invites={this.state.invites}
139+ />
137− : null
140+ ) : null}
138− }
141+
139−
140− {
141− online
142− ? <KeysAndMetadata pointDetails={ pointDetails } />
142+ {online ? <KeysAndMetadata pointDetails={pointDetails} /> : <div />}
143− : <div />
144− }
145− { issuedPointList }
143+ {issuedPointList}
146 </Col>
144 </Col>
147 </Row>
145 </Row>
148− )
146+ );
149 }
147 }
150}
148}
151
149
152−export default Point
150+export default Point;
src/bridge/views/Point/Actions.js+132−163
@@ -1,265 +1,234 @@
1−import React from 'react'
1+import React from 'react';
2−import * as need from '../../lib/need'
2+import * as need from '../../lib/need';
3−import {
4− ETH_ZERO_ADDR,
5− CURVE_ZERO_ADDR,
3+import { ETH_ZERO_ADDR, CURVE_ZERO_ADDR, eqAddr } from '../../lib/wallet';
6− eqAddr
7−} from '../../lib/wallet'
8−import { Row, Col, H2, P } from '../../components/Base'
4+import { Row, Col, H2, P } from '../../components/Base';
9−import { Button } from '../../components/Base'
5+import { Button } from '../../components/Base';
10−import { ROUTE_NAMES } from '../../lib/router'
6+import { ROUTE_NAMES } from '../../lib/router';
11−import { azimuth } from 'azimuth-js'
7+import { azimuth } from 'azimuth-js';
12
8
13const isPlanet = point =>
9const isPlanet = point =>
14− (azimuth.getPointSize(point) === azimuth.PointSize.Planet);
10+ azimuth.getPointSize(point) === azimuth.PointSize.Planet;
15−const isStar = point =>
16− (azimuth.getPointSize(point) === azimuth.PointSize.Star);
11+const isStar = point => azimuth.getPointSize(point) === azimuth.PointSize.Star;
17
12
18−const Actions = (props) => {
13+const Actions = props => {
19− const {
20− pushRoute, online,
21− point, pointDetails, invites
14+ const { pushRoute, online, point, pointDetails, invites } = props;
22− } = props
23
15
24 const addr = need.address(props);
16 const addr = need.address(props);
25
17
26 const isOwner = pointDetails.matchWith({
18 const isOwner = pointDetails.matchWith({
27 Nothing: _ => false,
19 Nothing: _ => false,
28− Just: details => eqAddr(details.value.owner, addr)
20+ Just: details => eqAddr(details.value.owner, addr),
29 });
21 });
30 const isActive = pointDetails.matchWith({
22 const isActive = pointDetails.matchWith({
31 Nothing: _ => false,
23 Nothing: _ => false,
32− Just: details => details.value.active
24+ Just: details => details.value.active,
33 });
25 });
34− const isActiveOwner = (isOwner && isActive);
26+ const isActiveOwner = isOwner && isActive;
35 const canSetSpawnProxy = isActiveOwner;
27 const canSetSpawnProxy = isActiveOwner;
36 const canSetManagementProxy = isActiveOwner;
28 const canSetManagementProxy = isActiveOwner;
37
29
30+ const canManage =
31+ isOwner ||
38− const canManage = (isOwner || pointDetails.matchWith({
32+ pointDetails.matchWith({
39− Nothing: _ => false,
33+ Nothing: _ => false,
40− Just: details => eqAddr(details.value.managementProxy, addr)
34+ Just: details => eqAddr(details.value.managementProxy, addr),
41− }));
35+ });
42− const canConfigureKeys = (canManage && isActive);
36+ const canConfigureKeys = canManage && isActive;
43
37
44− const canIssueChild =
45− pointDetails.matchWith({
38+ const canIssueChild = pointDetails.matchWith({
46− Nothing: () => false,
39+ Nothing: () => false,
47− Just: (details) => {
40+ Just: details => {
48− const hasPermission =
49− isOwner
50− || eqAddr(details.value.spawnProxy, addr)
41+ const hasPermission = isOwner || eqAddr(details.value.spawnProxy, addr);
51
42
52− const isBooted =
53− details.value.keyRevisionNumber > 0
43+ const isBooted = details.value.keyRevisionNumber > 0;
54
44
55− const isNotPlanet = !isPlanet(point)
45+ const isNotPlanet = !isPlanet(point);
56
46
57− return hasPermission && isBooted && isNotPlanet
47+ return hasPermission && isBooted && isNotPlanet;
58− }
48+ },
59− })
49+ });
60
50
61− const hasInvites =
62− invites.matchWith({
51+ const hasInvites = invites.matchWith({
63− Nothing: () => false,
52+ Nothing: () => false,
64− Just: (count) => (count.value > 0)
53+ Just: count => count.value > 0,
65− });
54+ });
66
55
67− const planet = isPlanet(point)
56+ const planet = isPlanet(point);
68− const star = isStar(point)
57+ const star = isStar(point);
69
58
70− const canTransfer =
71− pointDetails.matchWith({
59+ const canTransfer = pointDetails.matchWith({
72− Nothing: () => false,
60+ Nothing: () => false,
73− Just: (deets) =>
61+ Just: deets =>
74− eqAddr(deets.value.transferProxy, addr) ||
62+ eqAddr(deets.value.transferProxy, addr) ||
75− eqAddr(deets.value.owner, addr)
63+ eqAddr(deets.value.owner, addr),
76− })
64+ });
77
65
78− const canGenKeyfile =
79− pointDetails.matchWith({
66+ const canGenKeyfile = pointDetails.matchWith({
80− Nothing: () => false,
67+ Nothing: () => false,
81− Just: (deets) => {
68+ Just: deets => {
82− const hasPermission =
69+ const hasPermission =
83− eqAddr(deets.value.owner, addr)
70+ eqAddr(deets.value.owner, addr) ||
84− || eqAddr(deets.value.managementProxy, addr)
71+ eqAddr(deets.value.managementProxy, addr);
85
72
86− const isBooted =
87− deets.value.keyRevisionNumber > 0
73+ const isBooted = deets.value.keyRevisionNumber > 0;
88
74
89− return hasPermission && isBooted
75+ return hasPermission && isBooted;
90− }
76+ },
91− })
77+ });
92
78
93− const canAcceptTransfer =
79+ const canAcceptTransfer = pointDetails.matchWith({
94− pointDetails.matchWith({
95− Nothing: () => false,
80+ Nothing: () => false,
96− Just: (deets) =>
97− eqAddr(deets.value.transferProxy, addr)
81+ Just: deets => eqAddr(deets.value.transferProxy, addr),
98− })
82+ });
99
83
100− const canCancelTransfer =
84+ const canCancelTransfer = pointDetails.matchWith({
101− pointDetails.matchWith({
102− Nothing: () => false,
85+ Nothing: () => false,
103− Just: (deets) =>
86+ Just: deets =>
104− eqAddr(deets.value.owner, addr) &&
87+ eqAddr(deets.value.owner, addr) &&
105− !eqAddr(deets.value.transferProxy, ETH_ZERO_ADDR)
88+ !eqAddr(deets.value.transferProxy, ETH_ZERO_ADDR),
106− })
89+ });
107
90
108 const displayReminder = pointDetails.matchWith({
91 const displayReminder = pointDetails.matchWith({
109 Nothing: () => false,
92 Nothing: () => false,
110− Just: (deets) => {
93+ Just: deets => {
94+ return (
111− return deets.value.encryptionKey === CURVE_ZERO_ADDR
95+ deets.value.encryptionKey === CURVE_ZERO_ADDR &&
112− && deets.value.authenticationKey === CURVE_ZERO_ADDR
96+ deets.value.authenticationKey === CURVE_ZERO_ADDR
97+ );
113− }
98+ },
114− })
99+ });
115
100
116 let inviteAction = null;
101 let inviteAction = null;
117 if (planet) {
102 if (planet) {
118 inviteAction = (
103 inviteAction = (
119 <Button
104 <Button
120− disabled={(!isActiveOwner || !online || !hasInvites)}
105+ disabled={!isActiveOwner || !online || !hasInvites}
121 prop-size={'sm'}
106 prop-size={'sm'}
122 prop-type={'link'}
107 prop-type={'link'}
123− onClick={ () => {
108+ onClick={() => {
124 pushRoute(ROUTE_NAMES.INVITES_SEND);
109 pushRoute(ROUTE_NAMES.INVITES_SEND);
125− }}
110+ }}>
126− >
127− { 'Send invites (' }
111+ {'Send invites ('}
128− { invites.matchWith({
112+ {invites.matchWith({
129− Nothing: () => '?',
113+ Nothing: () => '?',
130− Just: (count) => count.value
114+ Just: count => count.value,
131− })
115+ })}
132− }
133− { ')' }
116+ {')'}
134 </Button>
117 </Button>
135 );
118 );
136 }
119 }
137 if (star) {
120 if (star) {
138 inviteAction = (
121 inviteAction = (
139 <Button
122 <Button
140− disabled={(!(isActiveOwner && canIssueChild) || !online)}
123+ disabled={!(isActiveOwner && canIssueChild) || !online}
141 prop-size={'sm'}
124 prop-size={'sm'}
142 prop-type={'link'}
125 prop-type={'link'}
143− onClick={ () => {
126+ onClick={() => {
144 pushRoute(ROUTE_NAMES.INVITES_MANAGE);
127 pushRoute(ROUTE_NAMES.INVITES_MANAGE);
145− }}
128+ }}>
146− >
147− { 'Manage invites' }
129+ {'Manage invites'}
148 </Button>
130 </Button>
149 );
131 );
150 }
132 }
151
133
152 return (
134 return (
153 <div>
135 <div>
154− <H2>{ 'Actions' }</H2>
136+ <H2>{'Actions'}</H2>
155− {
156− displayReminder
137+ {displayReminder ? (
157− ? <P>{`Before you can issue child points or generate your Arvo
138+ <P>{`Before you can issue child points or generate your Arvo
158 keyfile, you need to set your public keys.`}</P>
139 keyfile, you need to set your public keys.`}</P>
140+ ) : (
159− : ''
141+ ''
160− }
142+ )}
161 <Row>
143 <Row>
162 <Col className={'flex flex-column items-start col-md-4'}>
144 <Col className={'flex flex-column items-start col-md-4'}>
163 <Button
145 <Button
164 prop-size={'sm'}
146 prop-size={'sm'}
165 prop-type={'link'}
147 prop-type={'link'}
166− disabled={ (online || planet) && !canIssueChild }
148+ disabled={(online || planet) && !canIssueChild}
167− onClick={ () => {
149+ onClick={() => {
168− pushRoute(ROUTE_NAMES.ISSUE_CHILD)
150+ pushRoute(ROUTE_NAMES.ISSUE_CHILD);
169− }}
151+ }}>
170− >
171− { 'Issue child' }
152+ {'Issue child'}
172 </Button>
153 </Button>
173
154
174 <Button
155 <Button
175 prop-size={'sm'}
156 prop-size={'sm'}
176 prop-type={'link'}
157 prop-type={'link'}
177− disabled={ online && !canAcceptTransfer }
158+ disabled={online && !canAcceptTransfer}
178− onClick={ () => {
159+ onClick={() => {
179− pushRoute(ROUTE_NAMES.ACCEPT_TRANSFER)
160+ pushRoute(ROUTE_NAMES.ACCEPT_TRANSFER);
180− }}
161+ }}>
181− >
182− { 'Accept incoming transfer' }
162+ {'Accept incoming transfer'}
183 </Button>
163 </Button>
184
164
185 <Button
165 <Button
186 prop-size={'sm'}
166 prop-size={'sm'}
187 prop-type={'link'}
167 prop-type={'link'}
188− disabled={ online && !canCancelTransfer }
168+ disabled={online && !canCancelTransfer}
189− onClick={ () => {
169+ onClick={() => {
190− pushRoute(ROUTE_NAMES.CANCEL_TRANSFER)
170+ pushRoute(ROUTE_NAMES.CANCEL_TRANSFER);
191− }}
171+ }}>
192− >
193− { 'Cancel outgoing transfer' }
172+ {'Cancel outgoing transfer'}
194 </Button>
173 </Button>
195
174
196 <Button
175 <Button
197 prop-size={'sm'}
176 prop-size={'sm'}
198 prop-type={'link'}
177 prop-type={'link'}
199− disabled={ online && !canGenKeyfile }
178+ disabled={online && !canGenKeyfile}
200− onClick={ () => {
179+ onClick={() => {
201− pushRoute(ROUTE_NAMES.GEN_KEYFILE)
180+ pushRoute(ROUTE_NAMES.GEN_KEYFILE);
202− }}
181+ }}>
203− >
204− { 'Generate Arvo keyfile' }
182+ {'Generate Arvo keyfile'}
205 </Button>
183 </Button>
206−
207 </Col>
184 </Col>
208 <Col className={'flex flex-column items-start col-md-4'}>
185 <Col className={'flex flex-column items-start col-md-4'}>
209−
210 <Button
186 <Button
211− disabled={ online && !canSetSpawnProxy }
187+ disabled={online && !canSetSpawnProxy}
212 prop-size={'sm'}
188 prop-size={'sm'}
213 prop-type={'link'}
189 prop-type={'link'}
214− onClick={ () => {
190+ onClick={() => {
215− pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY)
191+ pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY);
216− }}
192+ }}>
217− >
218− { 'Change spawn proxy' }
193+ {'Change spawn proxy'}
219 </Button>
194 </Button>
220
195
221 <Button
196 <Button
222− disabled={ online && !canSetManagementProxy }
197+ disabled={online && !canSetManagementProxy}
223 prop-size={'sm'}
198 prop-size={'sm'}
224 prop-type={'link'}
199 prop-type={'link'}
225− onClick={ () => {
200+ onClick={() => {
226− pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY)
201+ pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY);
227− }}
202+ }}>
228− >
229− { 'Change management proxy' }
203+ {'Change management proxy'}
230 </Button>
204 </Button>
231
205
232 <Button
206 <Button
233− disabled={ online && !canConfigureKeys }
207+ disabled={online && !canConfigureKeys}
234 prop-size={'sm'}
208 prop-size={'sm'}
235 prop-type={'link'}
209 prop-type={'link'}
236− onClick={ () => {
210+ onClick={() => {
237− pushRoute(ROUTE_NAMES.SET_KEYS)
211+ pushRoute(ROUTE_NAMES.SET_KEYS);
238− }}
212+ }}>
239− >
240− { 'Set network keys' }
213+ {'Set network keys'}
241 </Button>
214 </Button>
242
215
243 <Button
216 <Button
244− disabled={ online && !canTransfer }
217+ disabled={online && !canTransfer}
245 prop-size={'sm'}
218 prop-size={'sm'}
246 prop-type={'link'}
219 prop-type={'link'}
247− onClick={ () => {
220+ onClick={() => {
248− pushRoute(ROUTE_NAMES.TRANSFER)
221+ pushRoute(ROUTE_NAMES.TRANSFER);
249− }}
222+ }}>
250− >
251− { 'Transfer' }
223+ {'Transfer'}
252 </Button>
224 </Button>
253−
254 </Col>
225 </Col>
255 <Col className={'flex flex-column items-start col-md-4'}>
226 <Col className={'flex flex-column items-start col-md-4'}>
256−
257− { inviteAction }
227+ {inviteAction}
258−
259 </Col>
228 </Col>
260 </Row>
229 </Row>
261 </div>
230 </div>
262− )
231+ );
263−}
232+};
264
233
265−export default Actions
234+export default Actions;
src/bridge/views/Point/KeysAndMetadata.js+124−146
@@ -1,181 +1,159 @@
1−import React from 'react'
1+import React from 'react';
2−import { Row, Col, H2, H4 } from '../../components/Base'
2+import { Row, Col, H2, H4 } from '../../components/Base';
3
3
4−import { ETH_ZERO_ADDR, eqAddr } from '../../lib/wallet'
4+import { ETH_ZERO_ADDR, eqAddr } from '../../lib/wallet';
5
5
6const NULL_KEY =
6const NULL_KEY =
7− '0x0000000000000000000000000000000000000000000000000000000000000000'
7+ '0x0000000000000000000000000000000000000000000000000000000000000000';
8
8
9−const renderAddress = (addr) =>
9+const renderAddress = addr =>
10− eqAddr(addr, ETH_ZERO_ADDR)
10+ eqAddr(addr, ETH_ZERO_ADDR) ? (
11− ? <div>
12− { '(not set)' }
11+ <div>{'(not set)'}</div>
13− </div>
12+ ) : (
14− : <div>
13+ <div>
14+ <div>
15− <div><code>{ addr.slice(0, 14) }</code></div>
15+ <code>{addr.slice(0, 14)}</code>
16+ </div>
17+ <div>
16− <div><code>{ addr.slice(14, 28) }</code></div>
18+ <code>{addr.slice(14, 28)}</code>
19+ </div>
20+ <div>
17− <div><code>{ addr.slice(28) }</code></div>
21+ <code>{addr.slice(28)}</code>
22+ </div>
18 </div>
23 </div>
19−
24+ );
25+
20−const renderNetworkKey = (hex) => {
26+const renderNetworkKey = hex => {
21− const sl = i => hex.slice(i, i + 4)
27+ const sl = i => hex.slice(i, i + 4);
22− const rowFrom = i => `${sl(i)}.${sl(i + 4)}.${sl(i + 8)}.${sl(i + 12)}`
28+ const rowFrom = i => `${sl(i)}.${sl(i + 4)}.${sl(i + 8)}.${sl(i + 12)}`;
23− return (
24− hex === NULL_KEY
29+ return hex === NULL_KEY ? (
25− ? <div>
26− { '(not set)' }
30+ <div>{'(not set)'}</div>
31+ ) : (
32+ <div>
33+ <div>
34+ <code>{hex.slice(0, 2)}</code>
35+ </div>
36+ <div>
37+ <code>{rowFrom(2)}</code>
38+ </div>
39+ <div>
40+ <code>{rowFrom(18)}</code>
41+ </div>
42+ <div>
43+ <code>{rowFrom(34)}</code>
27 </div>
44 </div>
28− : <div>
29− <div><code>{ hex.slice(0, 2) }</code></div>
30− <div>
31− <code>{ rowFrom(2) }</code>
32− </div>
33− <div>
34− <code>{ rowFrom(18) }</code>
35− </div>
36− <div>
45+ <div>
37− <code>{ rowFrom(34) }</code>
38− </div>
39− <div>
40− <code>{ rowFrom(50) }</code>
46+ <code>{rowFrom(50)}</code>
41− </div>
42 </div>
47 </div>
48+ </div>
43− )
49+ );
44−}
50+};
45
51
46−const KeysAndMetadata = (props) => {
52+const KeysAndMetadata = props => {
47− const { pointDetails } = props
53+ const { pointDetails } = props;
48
54
49 return (
55 return (
50 <Row>
56 <Row>
51 <Col>
57 <Col>
52− <H2>{ 'Ownership and Proxy Addresses' }</H2>
58+ <H2>{'Ownership and Proxy Addresses'}</H2>
53
59
54− <Row>
60+ <Row>
55− <Col className={'flex flex-column items-start col-md-3'}>
61+ <Col className={'flex flex-column items-start col-md-3'}>
56− <H4>{ 'Owner' }</H4>
62+ <H4>{'Owner'}</H4>
57− {
58− pointDetails.matchWith({
63+ {pointDetails.matchWith({
59 Nothing: () => <div />,
64 Nothing: () => <div />,
60− Just: (deets) => renderAddress(deets.value.owner)
65+ Just: deets => renderAddress(deets.value.owner),
61− })
66+ })}
62− }
63− </Col>
67+ </Col>
64−
65
68
66− <Col className={'flex flex-column items-start col-md-3'}>
69+ <Col className={'flex flex-column items-start col-md-3'}>
67− <H4>{ 'Transfer Proxy' }</H4>
70+ <H4>{'Transfer Proxy'}</H4>
68− {
69− pointDetails.matchWith({
71+ {pointDetails.matchWith({
70 Nothing: () => <div />,
72 Nothing: () => <div />,
71− Just: (deets) => renderAddress(deets.value.transferProxy)
73+ Just: deets => renderAddress(deets.value.transferProxy),
72− })
74+ })}
73− }
74− </Col>
75+ </Col>
75−
76
76
77− <Col className={'flex flex-column items-start col-md-3'}>
77+ <Col className={'flex flex-column items-start col-md-3'}>
78− <H4>{ 'Spawn Proxy' }</H4>
78+ <H4>{'Spawn Proxy'}</H4>
79− {
80− pointDetails.matchWith({
79+ {pointDetails.matchWith({
81 Nothing: () => <div />,
80 Nothing: () => <div />,
82− Just: (deets) => renderAddress(deets.value.spawnProxy)
81+ Just: deets => renderAddress(deets.value.spawnProxy),
83− })
82+ })}
84− }
85− </Col>
83+ </Col>
86
84
87−
88− <Col className={'flex flex-column items-start col-md-3'}>
85+ <Col className={'flex flex-column items-start col-md-3'}>
89− <H4>{ 'Mgmt Proxy' }</H4>
86+ <H4>{'Mgmt Proxy'}</H4>
90− {
91− pointDetails.matchWith({
87+ {pointDetails.matchWith({
92 Nothing: () => <div />,
88 Nothing: () => <div />,
93− Just: (deets) => renderAddress(deets.value.managementProxy)
89+ Just: deets => renderAddress(deets.value.managementProxy),
94− })
90+ })}
95− }
96− </Col>
91+ </Col>
97− </Row>
92+ </Row>
98−
99−
100− <UrbitNetworking pointDetails={ pointDetails } />
101−
102
93
94+ <UrbitNetworking pointDetails={pointDetails} />
103 </Col>
95 </Col>
104 </Row>
96 </Row>
105− )
97+ );
106−}
98+};
107
99
108const UrbitNetworking = props => {
100const UrbitNetworking = props => {
109− const { pointDetails } = props
101+ const { pointDetails } = props;
110
102
111 const booted = pointDetails.matchWith({
103 const booted = pointDetails.matchWith({
112 Nothing: _ => false,
104 Nothing: _ => false,
113− Just: (details) => details.value.keyRevisionNumber > 0
105+ Just: details => details.value.keyRevisionNumber > 0,
114− })
106+ });
115
107
116− const body =
108+ const body = !booted ? (
117− !booted
118− ? <div>
119− { '(not set)' }
109+ <div>{'(not set)'}</div>
120− </div>
110+ ) : (
121− : <Row>
111+ <Row>
122− <Col className={'flex flex-column items-start col-md-4'}>
112+ <Col className={'flex flex-column items-start col-md-4'}>
123− <H4>{ 'Authentication' }</H4>
113+ <H4>{'Authentication'}</H4>
124− {
125− pointDetails.matchWith({
114+ {pointDetails.matchWith({
126− Nothing: () => <div />,
115+ Nothing: () => <div />,
127− Just: (deets) =>
128− renderNetworkKey(deets.value.authenticationKey)
116+ Just: deets => renderNetworkKey(deets.value.authenticationKey),
129− })
117+ })}
130− }
131− </Col>
118+ </Col>
132−
119+
133− <Col className={'flex flex-column items-start col-md-4'}>
120+ <Col className={'flex flex-column items-start col-md-4'}>
134− <H4>{ 'Encryption' }</H4>
121+ <H4>{'Encryption'}</H4>
135− {
136− pointDetails.matchWith({
122+ {pointDetails.matchWith({
137− Nothing: () => <div />,
123+ Nothing: () => <div />,
138− Just: (deets) =>
139− renderNetworkKey(deets.value.encryptionKey)
124+ Just: deets => renderNetworkKey(deets.value.encryptionKey),
140− })
125+ })}
141− }
142− </Col>
126+ </Col>
143−
127+
144− <Col className={'flex flex-column items-start col-md-4'}>
128+ <Col className={'flex flex-column items-start col-md-4'}>
145− <H4>{ 'Revision' }</H4>
129+ <H4>{'Revision'}</H4>
146− {
147− pointDetails.matchWith({
130+ {pointDetails.matchWith({
148− Nothing: () => '-',
131+ Nothing: () => '-',
149− Just: (deets) => deets.value.keyRevisionNumber
132+ Just: deets => deets.value.keyRevisionNumber,
150− })
133+ })}
151− }
134+
152−
153−
154− <H4>{ 'Continuity era' }</H4>
135+ <H4>{'Continuity era'}</H4>
155− {
156− pointDetails.matchWith({
136+ {pointDetails.matchWith({
157− Nothing: () => '-',
137+ Nothing: () => '-',
158− Just: (deets) => deets.value.continuityNumber
138+ Just: deets => deets.value.continuityNumber,
159− })
139+ })}
160− }
140+
161−
162− <H4>{ 'Crypto suite version' }</H4>
141+ <H4>{'Crypto suite version'}</H4>
163− {
164− pointDetails.matchWith({
142+ {pointDetails.matchWith({
165− Nothing: () => '-',
143+ Nothing: () => '-',
166− Just: (deets) => deets.value.cryptoSuiteVersion
144+ Just: deets => deets.value.cryptoSuiteVersion,
167− })
145+ })}
168− }
169− </Col>
146+ </Col>
170− </Row>
147+ </Row>
148+ );
171
149
172 return (
150 return (
173 <div>
151 <div>
174− <H2>{ 'Public Keys' }</H2>
152+ <H2>{'Public Keys'}</H2>
175
153
176− { body }
154+ {body}
177 </div>
155 </div>
178− )
156+ );
179−}
157+};
180
158
181−export default KeysAndMetadata
159+export default KeysAndMetadata;
src/bridge/views/Points.js+146−138
@@ -1,28 +1,22 @@
1−import React from 'react'
1+import React from 'react';
2−import { Row, Col, H1, H2, P } from '../components/Base'
2+import { Row, Col, H1, H2, P } from '../components/Base';
3−import { Button } from '../components/Base'
3+import { Button } from '../components/Base';
4−import * as azimuth from 'azimuth-js'
4+import * as azimuth from 'azimuth-js';
5−
5+
6−import PointList from '../components/PointList'
6+import PointList from '../components/PointList';
7−import { NETWORK_NAMES } from '../lib/network'
7+import { NETWORK_NAMES } from '../lib/network';
8−import { ROUTE_NAMES } from '../lib/router'
8+import { ROUTE_NAMES } from '../lib/router';
9−import {
10− ETH_ZERO_ADDR,
9+import { ETH_ZERO_ADDR, eqAddr } from '../lib/wallet';
11− eqAddr
12− } from '../lib/wallet'
13
10
14const hasTransferProxy = (cache, point) =>
11const hasTransferProxy = (cache, point) =>
15− point in cache
16− ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR)
12+ point in cache ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR) : false;
17− : false
18
13
19class Points extends React.Component {
14class Points extends React.Component {
20−
21 constructor(props) {
15 constructor(props) {
22− super(props)
16+ super(props);
23
17
24− const { networkType } = props
18+ const { networkType } = props;
25− const loading = networkType !== NETWORK_NAMES.OFFLINE
19+ const loading = networkType !== NETWORK_NAMES.OFFLINE;
26
20
27 this.state = {
21 this.state = {
28 points: [],
22 points: [],
@@ -31,219 +25,233 @@ class Points extends React.Component {
31 voting: [],
25 voting: [],
32 spawning: [],
26 spawning: [],
33 eclipticOwner: false,
27 eclipticOwner: false,
34− loading: loading
28+ loading: loading,
35− }
29+ };
36 }
30 }
37
31
38 componentDidMount() {
32 componentDidMount() {
39− const { web3, wallet, contracts, addToPointCache } = this.props
33+ const { web3, wallet, contracts, addToPointCache } = this.props;
40
34
41 web3.chain(_ =>
35 web3.chain(_ =>
42− contracts.chain(ctrcs =>
36+ contracts.chain(ctrcs =>
43− wallet.chain(async wal => {
37+ wallet.chain(async wal => {
44− const addr = wal.address
38+ const addr = wal.address;
45
39
46− const points =
47− await azimuth.azimuth.getOwnedPoints(ctrcs, addr)
40+ const points = await azimuth.azimuth.getOwnedPoints(ctrcs, addr);
48
41
49− const incoming =
50− await azimuth.azimuth.getTransferringFor(ctrcs, addr)
42+ const incoming = await azimuth.azimuth.getTransferringFor(
43+ ctrcs,
44+ addr
45+ );
51
46
52− const managing =
53− await azimuth.azimuth.getManagerFor(ctrcs, addr)
47+ const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
54
48
55− const voting =
56− await azimuth.azimuth.getVotingFor(ctrcs, addr)
49+ const voting = await azimuth.azimuth.getVotingFor(ctrcs, addr);
57
50
58− const spawning =
59− await azimuth.azimuth.getSpawningFor(ctrcs, addr)
51+ const spawning = await azimuth.azimuth.getSpawningFor(ctrcs, addr);
60
52
61− const owner =
62− await azimuth.ecliptic.owner(ctrcs)
53+ const owner = await azimuth.ecliptic.owner(ctrcs);
63
54
64− const eclipticOwner = eqAddr(addr, owner)
55+ const eclipticOwner = eqAddr(addr, owner);
65
56
66− this.setState({
57+ this.setState({
67− points,
58+ points,
68− incoming,
59+ incoming,
69− managing,
60+ managing,
70− voting,
61+ voting,
71− spawning,
62+ spawning,
72− eclipticOwner,
63+ eclipticOwner,
73− loading: false
64+ loading: false,
74− })
65+ });
75
66
76− this.cachePoints(ctrcs, addToPointCache, points)
67+ this.cachePoints(ctrcs, addToPointCache, points);
77− })))
68+ })
69+ )
70+ );
78 }
71 }
79
72
80 cachePoints(contracts, addToPointCache, points) {
73 cachePoints(contracts, addToPointCache, points) {
81 points.forEach(point => {
74 points.forEach(point => {
75+ azimuth.azimuth
82− azimuth.azimuth.getPoint(contracts, point)
76+ .getPoint(contracts, point)
83− .then(details => addToPointCache({ [point]: details }))
77+ .then(details => addToPointCache({ [point]: details }));
84− })
78+ });
85 }
79 }
86
80
87 render() {
81 render() {
88− const { setPointCursor, pushRoute } = this.props
82+ const { setPointCursor, pushRoute } = this.props;
89− const { pointCache } = this.props
83+ const { pointCache } = this.props;
90−
84+
91− const { points, incoming, managing, voting, spawning, loading } = this.state
85+ const {
86+ points,
87+ incoming,
88+ managing,
89+ voting,
90+ spawning,
91+ loading,
92+ } = this.state;
92− const { eclipticOwner } = this.state
93+ const { eclipticOwner } = this.state;
93−
94+
94− const lookupPointButton =
95+ const lookupPointButton = (
95 <Row>
96 <Row>
96 <Col>
97 <Col>
97 <Button
98 <Button
98 prop-type={'link'}
99 prop-type={'link'}
99 prop-size={'md'}
100 prop-size={'md'}
100− onClick={ () => pushRoute(ROUTE_NAMES.VIEW_SHIP) }
101+ onClick={() => pushRoute(ROUTE_NAMES.VIEW_SHIP)}>
101− >
102− { 'View a point →' }
102+ {'View a point →'}
103 </Button>
103 </Button>
104− <P>
105− { "View a point on Azimuth." }
104+ <P>{'View a point on Azimuth.'}</P>
106− </P>
107 </Col>
105 </Col>
108 </Row>
106 </Row>
107+ );
109
108
110 const createGalaxyButton =
109 const createGalaxyButton =
111− eclipticOwner === false
110+ eclipticOwner === false ? (
112− ? <div />
111+ <div />
112+ ) : (
113− : <Row>
113+ <Row>
114 <Col>
114 <Col>
115 <Button
115 <Button
116 prop-type={'link'}
116 prop-type={'link'}
117 prop-size={'md'}
117 prop-size={'md'}
118− onClick={ () => pushRoute(ROUTE_NAMES.CREATE_GALAXY) }
118+ onClick={() => pushRoute(ROUTE_NAMES.CREATE_GALAXY)}>
119− >
120− { 'Create a galaxy →' }
119+ {'Create a galaxy →'}
121 </Button>
120 </Button>
122 <P>
121 <P>
122+ {
123− { "You have the authority to create a new Galaxy and you can do so here." }
123+ 'You have the authority to create a new Galaxy and you can do so here.'
124+ }
124 </P>
125 </P>
125 </Col>
126 </Col>
126 </Row>
127 </Row>
128+ );
127
129
128 const outgoing = points.filter(point =>
130 const outgoing = points.filter(point =>
129 hasTransferProxy(pointCache, point)
131 hasTransferProxy(pointCache, point)
130− )
132+ );
131
133
132 const outgoingPoints =
134 const outgoingPoints =
133− outgoing.length !== 0
135+ outgoing.length !== 0 ? (
134− ? <React.Fragment>
136+ <React.Fragment>
135− <H2>{ 'Outgoing Transfers' }</H2>
137+ <H2>{'Outgoing Transfers'}</H2>
136 <P>
138 <P>
137− { `You own these points until the recipient accepts the incoming
139+ {`You own these points until the recipient accepts the incoming
138− transfer. You may cancel the transfer until accepted.` }
140+ transfer. You may cancel the transfer until accepted.`}
139 </P>
141 </P>
140 <PointList
142 <PointList
141− setPointCursor={ setPointCursor }
143+ setPointCursor={setPointCursor}
142− routeHandler={ pushRoute }
144+ routeHandler={pushRoute}
143− points={ outgoing }
145+ points={outgoing}
144 />
146 />
145 </React.Fragment>
147 </React.Fragment>
148+ ) : (
146− : <div />
149+ <div />
150+ );
147
151
148 const incomingPoints =
152 const incomingPoints =
149− incoming.length !== 0
153+ incoming.length !== 0 ? (
150− ? <React.Fragment>
154+ <React.Fragment>
151− <H2>{ 'Incoming Transfers' }</H2>
155+ <H2>{'Incoming Transfers'}</H2>
152 <P>
156 <P>
153− { `You do not own these points until you accept the incoming transfer.
157+ {`You do not own these points until you accept the incoming transfer.
154− You may reject any incoming transfers.` }
158+ You may reject any incoming transfers.`}
155 </P>
159 </P>
156 <PointList
160 <PointList
157− setPointCursor={ setPointCursor }
161+ setPointCursor={setPointCursor}
158− routeHandler={ pushRoute }
162+ routeHandler={pushRoute}
159− points={ incoming }
163+ points={incoming}
160 />
164 />
161 </React.Fragment>
165 </React.Fragment>
166+ ) : (
162− : <div />
167+ <div />
168+ );
163
169
164 const managingPoints =
170 const managingPoints =
165− managing.length !== 0
171+ managing.length !== 0 ? (
166− ? <React.Fragment>
172+ <React.Fragment>
167− <H2>{ 'You Are a Management Proxy For' }</H2>
173+ <H2>{'You Are a Management Proxy For'}</H2>
168 <P>
174 <P>
169− { `You can configure or set network keys and conduct sponsorship
175+ {`You can configure or set network keys and conduct sponsorship
170− related operations for these points.` }
176+ related operations for these points.`}
171 </P>
177 </P>
172 <PointList
178 <PointList
173− setPointCursor={ setPointCursor }
179+ setPointCursor={setPointCursor}
174− routeHandler={ pushRoute }
180+ routeHandler={pushRoute}
175− points={ managing }
181+ points={managing}
176 />
182 />
177 </React.Fragment>
183 </React.Fragment>
184+ ) : (
178− : <div />
185+ <div />
186+ );
179
187
180 const votingPoints =
188 const votingPoints =
181− voting.length !== 0
189+ voting.length !== 0 ? (
182− ? <React.Fragment>
190+ <React.Fragment>
183− <H2>{ 'You Are a Voting Proxy For' }</H2>
191+ <H2>{'You Are a Voting Proxy For'}</H2>
184 <P>
192 <P>
185− { `Since you are part of the Galactic Senate, you can cast votes on
193+ {`Since you are part of the Galactic Senate, you can cast votes on
186− new Azimuth proposals on behalf of these points.` }
194+ new Azimuth proposals on behalf of these points.`}
187 </P>
195 </P>
188 <PointList
196 <PointList
189− setPointCursor={ setPointCursor }
197+ setPointCursor={setPointCursor}
190− routeHandler={ pushRoute }
198+ routeHandler={pushRoute}
191− points={ voting }
199+ points={voting}
192 />
200 />
193 </React.Fragment>
201 </React.Fragment>
202+ ) : (
194− : <div />
203+ <div />
204+ );
195
205
196 const spawningPoints =
206 const spawningPoints =
197− spawning.length !== 0
207+ spawning.length !== 0 ? (
198− ? <React.Fragment>
208+ <React.Fragment>
199− <H2>{ 'You Are a Spawn Proxy For' }</H2>
209+ <H2>{'You Are a Spawn Proxy For'}</H2>
200− <P>
201− { `You can create new child ships under these points.` }
210+ <P>{`You can create new child ships under these points.`}</P>
202− </P>
203 <PointList
211 <PointList
204− setPointCursor={ setPointCursor }
212+ setPointCursor={setPointCursor}
205− routeHandler={ pushRoute }
213+ routeHandler={pushRoute}
206− points={ spawning }
214+ points={spawning}
207 />
215 />
208 </React.Fragment>
216 </React.Fragment>
217+ ) : (
209− : <div />
218+ <div />
219+ );
210
220
211 return (
221 return (
212 <Row>
222 <Row>
213 <Col>
223 <Col>
214− <H1>{ 'Points' }</H1>
224+ <H1>{'Points'}</H1>
215
225
216 <P>{`A point is an identity on the Ethereum blockchain.
226 <P>{`A point is an identity on the Ethereum blockchain.
217 Points declare keys for ships on the Arvo network.`}</P>
227 Points declare keys for ships on the Arvo network.`}</P>
218
228
229+ {lookupPointButton}
219
230
220− { lookupPointButton }
221−
222− { createGalaxyButton }
231+ {createGalaxyButton}
223
232
224− { incomingPoints }
233+ {incomingPoints}
225
234
226− { outgoingPoints }
235+ {outgoingPoints}
227
236
228− <H2>{ 'Your Points' }</H2>
237+ <H2>{'Your Points'}</H2>
229
238
230− <PointList
239+ <PointList
231− setPointCursor={ setPointCursor }
240+ setPointCursor={setPointCursor}
232− routeHandler={ pushRoute }
241+ routeHandler={pushRoute}
233− points={ points }
242+ points={points}
234− loading={ loading }
243+ loading={loading}
235− />
244+ />
236−
237− { managingPoints }
238
245
239− { votingPoints }
246+ {managingPoints}
240
247
241− { spawningPoints }
248+ {votingPoints}
242
249
250+ {spawningPoints}
243 </Col>
251 </Col>
244 </Row>
252 </Row>
245− )
253+ );
246 }
254 }
247}
255}
248
256
249−export default Points
257+export default Points;
src/bridge/views/PrivateKey.js+51−56
@@ -1,83 +1,78 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Button } from '../components/Base'
3+import { Button } from '../components/Base';
4−import { RequiredInput, InnerLabel, InputCaption } from '../components/Base'
4+import { RequiredInput, InnerLabel, InputCaption } from '../components/Base';
5−import { Row, Col, H1 } from '../components/Base'
5+import { Row, Col, H1 } from '../components/Base';
6
6
7−import { ROUTE_NAMES } from '../lib/router'
7+import { ROUTE_NAMES } from '../lib/router';
8−import { EthereumWallet } from '../lib/wallet'
8+import { EthereumWallet } from '../lib/wallet';
9
9
10class PrivateKey extends React.Component {
10class PrivateKey extends React.Component {
11−
12 constructor(props) {
11 constructor(props) {
13− super(props)
12+ super(props);
14
13
15 this.state = {
14 this.state = {
16 privateKey: '',
15 privateKey: '',
17− }
16+ };
18
17
19− this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this)
18+ this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this);
20 }
19 }
21
20
22 handlePrivateKeyInput(privateKey) {
21 handlePrivateKeyInput(privateKey) {
23− this.setState({ privateKey })
22+ this.setState({ privateKey });
24− this.constructWallet(privateKey)
23+ this.constructWallet(privateKey);
25 }
24 }
26
25
27 constructWallet(privateKey) {
26 constructWallet(privateKey) {
28− const { setWallet } = this.props
27+ const { setWallet } = this.props;
29 if (/^[0-9A-Fa-f]{64}$/g.test(privateKey) === true) {
28 if (/^[0-9A-Fa-f]{64}$/g.test(privateKey) === true) {
30− const sec = Buffer.from(privateKey, 'hex')
29+ const sec = Buffer.from(privateKey, 'hex');
31− const wallet = new EthereumWallet(sec)
30+ const wallet = new EthereumWallet(sec);
32− setWallet(Maybe.Just(wallet))
31+ setWallet(Maybe.Just(wallet));
33 } else {
32 } else {
34− setWallet(Maybe.Nothing())
33+ setWallet(Maybe.Nothing());
35 }
34 }
36 }
35 }
37
36
38 render() {
37 render() {
39− const { pushRoute, popRoute, wallet } = this.props
38+ const { pushRoute, popRoute, wallet } = this.props;
40− const { privateKey } = this.state
39+ const { privateKey } = this.state;
41
40
42 return (
41 return (
43− <Row>
42+ <Row>
44− <Col className={'measure-md'}>
43+ <Col className={'measure-md'}>
45− <H1 className={'mb-4'}>{ 'Enter Your Private Key' }</H1>
44+ <H1 className={'mb-4'}>{'Enter Your Private Key'}</H1>
46− <InputCaption>
45+ <InputCaption>
47− { `Please enter your raw Ethereum private key here.` }
46+ {`Please enter your raw Ethereum private key here.`}
48− </InputCaption>
47+ </InputCaption>
49−
50− <RequiredInput
51− className='pt-8 mt-8'
52− prop-size='md'
53− prop-format='innerLabel'
54− type='password'
55− name='privateKey'
56− onChange={ this.handlePrivateKeyInput }
57− value={ privateKey }
58− autocomplete='off'
59− autoFocus>
60− <InnerLabel>{'Private Key'}</InnerLabel>
61− </RequiredInput>
62−
63− <Button
64− className={'mt-10'}
65− prop-size={ 'wide lg' }
66− disabled={ Maybe.Nothing.hasInstance(wallet) }
67− onClick={ () => {
68− popRoute()
69− pushRoute(ROUTE_NAMES.SHIPS)
70− }
71− }
72− >
73− { 'Continue →' }
74− </Button>
75− </Col>
76− </Row>
77
48
49+ <RequiredInput
50+ className="pt-8 mt-8"
51+ prop-size="md"
52+ prop-format="innerLabel"
53+ type="password"
54+ name="privateKey"
55+ onChange={this.handlePrivateKeyInput}
56+ value={privateKey}
57+ autocomplete="off"
58+ autoFocus>
59+ <InnerLabel>{'Private Key'}</InnerLabel>
60+ </RequiredInput>
78
61
62+ <Button
63+ className={'mt-10'}
64+ prop-size={'wide lg'}
65+ disabled={Maybe.Nothing.hasInstance(wallet)}
66+ onClick={() => {
67+ popRoute();
68+ pushRoute(ROUTE_NAMES.SHIPS);
69+ }}>
70+ {'Continue →'}
71+ </Button>
72+ </Col>
73+ </Row>
79− )
74+ );
80 }
75 }
81}
76}
82
77
83−export default PrivateKey
78+export default PrivateKey;
src/bridge/views/SentTransaction.js+104−113
@@ -1,165 +1,158 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as need from '../lib/need'
3+import * as need from '../lib/need';
4−import { Row, Col, H1, H3, P, Warning, Anchor } from '../components/Base'
4+import { Row, Col, H1, H3, P, Warning, Anchor } from '../components/Base';
5−import { Button } from '../components/Base'
5+import { Button } from '../components/Base';
6−
7−import { ROUTE_NAMES } from '../lib/router'
8−import { BRIDGE_ERROR, renderTxnError } from '../lib/error'
9−import { NETWORK_NAMES } from '../lib/network'
10
6
7+import { ROUTE_NAMES } from '../lib/router';
8+import { BRIDGE_ERROR, renderTxnError } from '../lib/error';
9+import { NETWORK_NAMES } from '../lib/network';
11
10
12class Success extends React.Component {
11class Success extends React.Component {
13−
14 constructor(props) {
12 constructor(props) {
15− super(props)
13+ super(props);
16
14
17 this.state = {
15 this.state = {
18 pending: '.',
16 pending: '.',
19− interval: null
17+ interval: null,
20− }
18+ };
21 }
19 }
22
20
23 componentDidMount() {
21 componentDidMount() {
24− const nextDot = {'.': '..',
22+ const nextDot = { '.': '..', '..': '...', '...': '.' };
25− '..': '...',
26− '...': '.'}
27
23
28 const interval = setInterval(() => {
24 const interval = setInterval(() => {
29− this.setState(({ pending }) => ({pending: nextDot[pending]}))
25+ this.setState(({ pending }) => ({ pending: nextDot[pending] }));
30− }, 1000)
26+ }, 1000);
31− this.setState({interval: interval})
27+ this.setState({ interval: interval });
32 }
28 }
33
29
34 componentWillUnmount() {
30 componentWillUnmount() {
35− clearInterval(this.state.interval)
31+ clearInterval(this.state.interval);
36 }
32 }
37
33
38 render() {
34 render() {
39−
40− const { props, state } = this
35+ const { props, state } = this;
41− const { networkType, hash, txnConfirmations } = props
36+ const { networkType, hash, txnConfirmations } = props;
42− const { pending } = state
37+ const { pending } = state;
43
38
44 const esvisible =
39 const esvisible =
45− networkType === NETWORK_NAMES.ROPSTEN ||
40+ networkType === NETWORK_NAMES.ROPSTEN ||
46− networkType === NETWORK_NAMES.MAINNET
41+ networkType === NETWORK_NAMES.MAINNET;
47
42
48 const esdomain =
43 const esdomain =
49− networkType === NETWORK_NAMES.ROPSTEN
44+ networkType === NETWORK_NAMES.ROPSTEN
50− ? "ropsten.etherscan.io"
45+ ? 'ropsten.etherscan.io'
51− : "etherscan.io"
46+ : 'etherscan.io';
52
47
53 const esmessage =
48 const esmessage =
54− esvisible === true
49+ esvisible === true
55− ? "If you’d like to keep track of it, click the Etherscan link below."
50+ ? 'If you’d like to keep track of it, click the Etherscan link below.'
56− : ''
51+ : '';
57
52
58 const esanchor =
53 const esanchor =
59− esvisible === false
54+ esvisible === false ? null : (
60− ? null
61− : <Anchor
55+ <Anchor
62 className={'mb-4 mt-1'}
56 className={'mb-4 mt-1'}
63 prop-size={'sm'}
57 prop-size={'sm'}
64 target={'_blank'}
58 target={'_blank'}
65 href={`https://${esdomain}/tx/${hash}`}>
59 href={`https://${esdomain}/tx/${hash}`}>
66− {'View on Etherscan ↗'}
60+ {'View on Etherscan ↗'}
67− </Anchor>
61+ </Anchor>
62+ );
68
63
69− const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(0)
64+ const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(
65+ 0
66+ );
70
67
71− const requiredConfirmations = 1
68+ const requiredConfirmations = 1;
72
69
70+ const status =
73− const status = confirmations < requiredConfirmations ?
71+ confirmations < requiredConfirmations
72+ ? `Pending${pending}`
74− `Pending${pending}` : `Confirmed! (x${confirmations} confirmations)!`
73+ : `Confirmed! (x${confirmations} confirmations)!`;
75
74
76 return (
75 return (
77 <Row>
76 <Row>
78 <Col>
77 <Col>
79− <H1>{ 'Your Transaction was Sent' }</H1>
78+ <H1>{'Your Transaction was Sent'}</H1>
80−
81− <P>
82− {
83− `We sent your transaction to the chain. It can take some time to
84− execute, especially if the network is busy. ${esmessage}`
85− }
86− </P>
87
79
88− <H3>{ 'Transaction Hash' }</H3>
89 <P>
80 <P>
90− { hash }
81+ {`We sent your transaction to the chain. It can take some time to
82+ execute, especially if the network is busy. ${esmessage}`}
91 </P>
83 </P>
92
84
93− <H3>{ 'Transaction Status' }</H3>
85+ <H3>{'Transaction Hash'}</H3>
94− <P>
86+ <P>{hash}</P>
95− { status }
96− </P>
97− <P>
98− { esanchor }
99− </P>
100
87
88+ <H3>{'Transaction Status'}</H3>
89+ <P>{status}</P>
90+ <P>{esanchor}</P>
101 </Col>
91 </Col>
102 </Row>
92 </Row>
103− )
93+ );
104 }
94 }
105}
95}
106
96
107−const Failure = (props) =>
97+const Failure = props => (
108−
109− <Row>
98+ <Row>
110− <Col>
99+ <Col>
111− <H1>{ 'Error!' }</H1>
100+ <H1>{'Error!'}</H1>
112−
101+
113−
114− <Warning>
102+ <Warning>
115− <H3>
116− {
117− 'There was an error sending your transaction.'
103+ <H3>{'There was an error sending your transaction.'}</H3>
118− }
119− </H3>
120− { renderTxnError(props.web3, props.message) }
104+ {renderTxnError(props.web3, props.message)}
121− </Warning>
105+ </Warning>
122− </Col>
106+ </Col>
123− </Row>
107+ </Row>
124−
108+);
109+
125−const SentTransaction = (props) => {
110+const SentTransaction = props => {
126− const { txnHashCursor, networkType, popRoute, pushRoute, txnConfirmations} = props
111+ const {
112+ txnHashCursor,
113+ networkType,
114+ popRoute,
115+ pushRoute,
116+ txnConfirmations,
117+ } = props;
127− const { setPointCursor, pointCursor } = props
118+ const { setPointCursor, pointCursor } = props;
128−
119+
129− const promptKeyfile = props.routeData && props.routeData.promptKeyfile
120+ const promptKeyfile = props.routeData && props.routeData.promptKeyfile;
130
121
131 const w3 = need.web3(props);
122 const w3 = need.web3(props);
132
123
133 const result = txnHashCursor.matchWith({
124 const result = txnHashCursor.matchWith({
125+ Nothing: _ => {
134− Nothing: _ => { throw BRIDGE_ERROR.MISSING_TXN },
126+ throw BRIDGE_ERROR.MISSING_TXN;
127+ },
135− Just: res => res.value
128+ Just: res => res.value,
136− })
129+ });
137
130
138 const body = result.matchWith({
131 const body = result.matchWith({
139− Error: message => <Failure web3={ w3 } message={ message.value } />,
132+ Error: message => <Failure web3={w3} message={message.value} />,
140− Ok: hash =>
133+ Ok: hash => (
141 <Success
134 <Success
142− hash={ hash.value }
135+ hash={hash.value}
143− networkType={ networkType }
136+ networkType={networkType}
144− txnConfirmations={ txnConfirmations }
137+ txnConfirmations={txnConfirmations}
145 />
138 />
139+ ),
146− })
140+ });
147
141
148− const ok =
142+ const ok = (
149 <Row>
143 <Row>
150 <Col>
144 <Col>
151 <Button
145 <Button
152 prop-type={'link'}
146 prop-type={'link'}
153− onClick={
147+ onClick={() => {
154− () => {
155− setPointCursor(pointCursor)
148+ setPointCursor(pointCursor);
156− popRoute()
149+ popRoute();
157− }
158− }>
150+ }}>
159− { 'Ok →' }
151+ {'Ok →'}
160 </Button>
152 </Button>
161 </Col>
153 </Col>
162 </Row>
154 </Row>
155+ );
163
156
164 let keyfile;
157 let keyfile;
165
158
@@ -169,26 +162,24 @@ const SentTransaction = (props) => {
169 <Col>
162 <Col>
170 <Button
163 <Button
171 prop-type={'link'}
164 prop-type={'link'}
172− onClick={
165+ onClick={() => {
173− () => {
174− popRoute()
166+ popRoute();
175− pushRoute(ROUTE_NAMES.GEN_KEYFILE)
167+ pushRoute(ROUTE_NAMES.GEN_KEYFILE);
176− }
177− }>
168+ }}>
178− { 'Download Keyfile →' }
169+ {'Download Keyfile →'}
179 </Button>
170 </Button>
180 </Col>
171 </Col>
181 </Row>
172 </Row>
182− )
173+ );
183 }
174 }
184
175
185 return (
176 return (
186 <div>
177 <div>
187− { body }
178+ {body}
188− { ok }
179+ {ok}
189− { keyfile }
180+ {keyfile}
190 </div>
181 </div>
191− )
182+ );
192−}
183+};
193
184
194−export default SentTransaction
185+export default SentTransaction;
src/bridge/views/SetKeys.js+81−70
@@ -1,20 +1,20 @@
1−import React from 'react'
1+import React from 'react';
2−import Maybe from 'folktale/maybe'
2+import Maybe from 'folktale/maybe';
3−import * as azimuth from 'azimuth-js'
3+import * as azimuth from 'azimuth-js';
4−import * as ob from 'urbit-ob'
4+import * as ob from 'urbit-ob';
5−import * as need from '../lib/need'
5+import * as need from '../lib/need';
6−import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base'
6+import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base';
7
7
8−import StatelessTransaction from '../components/StatelessTransaction'
8+import StatelessTransaction from '../components/StatelessTransaction';
9−import { attemptSeedDerivation } from '../lib/keys'
9+import { attemptSeedDerivation } from '../lib/keys';
10
10
11−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
11+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
12
12
13−import { addHexPrefix } from '../lib/wallet'
13+import { addHexPrefix } from '../lib/wallet';
14
14
15class SetKeys extends React.Component {
15class SetKeys extends React.Component {
16 constructor(props) {
16 constructor(props) {
17− super(props)
17+ super(props);
18
18
19 const point = need.pointCursor(props);
19 const point = need.pointCursor(props);
20
20
@@ -27,11 +27,11 @@ class SetKeys extends React.Component {
27 cryptoSuiteVersion: 1,
27 cryptoSuiteVersion: 1,
28 discontinuity: false,
28 discontinuity: false,
29 isManagementSeed: false,
29 isManagementSeed: false,
30− }
30+ };
31
31
32 this.toggleDiscontinuity = this.toggleDiscontinuity.bind(this);
32 this.toggleDiscontinuity = this.toggleDiscontinuity.bind(this);
33 // Transaction
33 // Transaction
34− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
34+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
35 }
35 }
36
36
37 toggleDiscontinuity() {
37 toggleDiscontinuity() {
@@ -39,39 +39,56 @@ class SetKeys extends React.Component {
39 }
39 }
40
40
41 componentDidMount() {
41 componentDidMount() {
42− const { props } = this
42+ const { props } = this;
43
43
44− this.deriveSeed()
44+ this.deriveSeed();
45
45
46 const addr = need.address(props);
46 const addr = need.address(props);
47
47
48− this.determineManagementSeed(props.contracts.value, addr)
48+ this.determineManagementSeed(props.contracts.value, addr);
49 }
49 }
50
50
51 async determineManagementSeed(ctrcs, addr) {
51 async determineManagementSeed(ctrcs, addr) {
52− const managing =
53− await azimuth.azimuth.getManagerFor(ctrcs, addr)
52+ const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
54
53
55 this.setState({
54 this.setState({
56− isManagementSeed: managing.length !== 0
55+ isManagementSeed: managing.length !== 0,
57− })
56+ });
58 }
57 }
59
58
60 //TODO use web3.utils.randomHex when it gets fixed, see web3.js#1490
59 //TODO use web3.utils.randomHex when it gets fixed, see web3.js#1490
61 randomHex(len) {
60 randomHex(len) {
62− let hex = "";
61+ let hex = '';
63
62
64 for (var i = 0; i < len; i++) {
63 for (var i = 0; i < len; i++) {
65− hex = hex + ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
64+ hex =
65+ hex +
66+ [
67+ '0',
68+ '1',
69+ '2',
70+ '3',
71+ '4',
72+ '5',
73+ '6',
74+ '7',
75+ '8',
76+ '9',
77+ 'A',
78+ 'B',
79+ 'C',
80+ 'D',
81+ 'E',
82+ 'F',
66− 'C', 'D', 'E', 'F'][Math.floor(Math.random() * 16)]
83+ ][Math.floor(Math.random() * 16)];
67 }
84 }
68
85
69 return hex;
86 return hex;
70 }
87 }
71
88
72 async deriveSeed() {
89 async deriveSeed() {
73− const next = true
90+ const next = true;
74− let seed = await attemptSeedDerivation(next, this.props)
91+ let seed = await attemptSeedDerivation(next, this.props);
75 let nondeterministicSeed = false;
92 let nondeterministicSeed = false;
76
93
77 if (seed.getOrElse('') === '') {
94 if (seed.getOrElse('') === '') {
@@ -81,25 +98,25 @@ class SetKeys extends React.Component {
81
98
82 this.setState({
99 this.setState({
83 networkSeed: seed.getOrElse(''),
100 networkSeed: seed.getOrElse(''),
84− nondeterministicSeed: nondeterministicSeed
101+ nondeterministicSeed: nondeterministicSeed,
85− })
102+ });
86 }
103 }
87
104
88 createUnsignedTxn() {
105 createUnsignedTxn() {
89− const { state, props } = this
106+ const { state, props } = this;
90
107
91 const validContracts = need.contracts(props);
108 const validContracts = need.contracts(props);
92
109
93 const validPoint = need.pointCursor(props);
110 const validPoint = need.pointCursor(props);
94
111
95− const hexRegExp = /[0-9A-Fa-f]{64}/g
112+ const hexRegExp = /[0-9A-Fa-f]{64}/g;
96
113
97 if (hexRegExp.test(state.networkSeed)) {
114 if (hexRegExp.test(state.networkSeed)) {
98 // derive network keys
115 // derive network keys
99− const pair = kg.deriveNetworkKeys(state.networkSeed)
116+ const pair = kg.deriveNetworkKeys(state.networkSeed);
100
117
101− const pencr = addHexPrefix(pair.crypt.public)
118+ const pencr = addHexPrefix(pair.crypt.public);
102− const pauth = addHexPrefix(pair.auth.public)
119+ const pauth = addHexPrefix(pair.auth.public);
103
120
104 const txn = azimuth.ecliptic.configureKeys(
121 const txn = azimuth.ecliptic.configureKeys(
105 validContracts,
122 validContracts,
@@ -108,20 +125,19 @@ class SetKeys extends React.Component {
108 pauth,
125 pauth,
109 1,
126 1,
110 state.discontinuity
127 state.discontinuity
111− )
128+ );
112
129
113− return Maybe.Just(txn)
130+ return Maybe.Just(txn);
114 }
131 }
115
132
116− return Maybe.Nothing()
133+ return Maybe.Nothing();
117 }
134 }
118
135
119 render() {
136 render() {
120− const { props, state } = this
137+ const { props, state } = this;
121
138
122 const canGenerate =
139 const canGenerate =
123− state.networkSeed.length === 64
124− && state.networkSeed.length === 64
140+ state.networkSeed.length === 64 && state.networkSeed.length === 64;
125
141
126 const pointDetails = need.fromPointCache(props, state.point);
142 const pointDetails = need.fromPointCache(props, state.point);
127
143
@@ -129,59 +145,54 @@ class SetKeys extends React.Component {
129 <Row>
145 <Row>
130 <Col>
146 <Col>
131 <H1>
147 <H1>
132− { 'Set Network Keys For ' } <code>{ `${ob.patp(state.point)}` }</code>
148+ {'Set Network Keys For '} <code>{`${ob.patp(state.point)}`}</code>
133 </H1>
149 </H1>
134
150
135 <P className="mt-10">
151 <P className="mt-10">
136− {
137− `Set new authentication and encryption keys for your Arvo ship.`
152+ {`Set new authentication and encryption keys for your Arvo ship.`}
138− }
139 </P>
153 </P>
140
154
141− { state.nondeterministicSeed &&
155+ {state.nondeterministicSeed && (
142 <Warning>
156 <Warning>
143 <h3 className={'mb-2'}>{'Warning'}</h3>
157 <h3 className={'mb-2'}>{'Warning'}</h3>
144− {
145− `Your network seed could not be derived automatically. We've
158+ {`Your network seed could not be derived automatically. We've
146 generated a random one for you, so you must download your Arvo
159 generated a random one for you, so you must download your Arvo
147− keyfile during this session after setting your keys.`
160+ keyfile during this session after setting your keys.`}
148− }
161+ </Warning>
162+ )}
163+
164+ {pointDetails.keyRevisionNumber === 0 ? (
165+ <Warning>
166+ <h3 className={'mb-2'}>{'Warning'}</h3>
167+ {'Once these keys have been set, your point is considered ' +
168+ "'linked'. This operation cannot be undone."}
149 </Warning>
169 </Warning>
150− }
170+ ) : (
151−
152− { pointDetails.keyRevisionNumber === 0
153− ? <Warning>
154− <h3 className={'mb-2'}>{'Warning'}</h3>
155− {
156− 'Once these keys have been set, your point is considered ' +
157− "'linked'. This operation cannot be undone."
158− }
159− </Warning>
160− : <div /> }
171+ <div />
172+ )}
161
173
162 <CheckboxButton
174 <CheckboxButton
163− className='mt-8'
175+ className="mt-8"
164− onToggle={ this.toggleDiscontinuity }
176+ onToggle={this.toggleDiscontinuity}
165− state={ state.discontinuity }
177+ state={state.discontinuity}
166 label={`I have lost important off-chain data relating to this point
178 label={`I have lost important off-chain data relating to this point
167 and need to do a hard reset.
179 and need to do a hard reset.
168− (For example, rebooting an Arvo ship.)`}
180+ (For example, rebooting an Arvo ship.)`}></CheckboxButton>
169− ></CheckboxButton>
170
181
171 <StatelessTransaction
182 <StatelessTransaction
172 // Upper scope
183 // Upper scope
173 {...props}
184 {...props}
174 // Tx
185 // Tx
175− canGenerate={ canGenerate }
186+ canGenerate={canGenerate}
176 createUnsignedTxn={this.createUnsignedTxn}
187 createUnsignedTxn={this.createUnsignedTxn}
177− networkSeed={ state.networkSeed }
188+ networkSeed={state.networkSeed}
178− newRevision={ (parseInt(pointDetails.keyRevisionNumber) + 1) }
189+ newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
179− setNetworkSeedCache={ props.setNetworkSeedCache } />
190+ setNetworkSeedCache={props.setNetworkSeedCache}
180−
191+ />
181 </Col>
192 </Col>
182 </Row>
193 </Row>
183− )
194+ );
184 }
195 }
185}
196}
186
197
187−export default SetKeys
198+export default SetKeys;
src/bridge/views/SetProxy.js+97−102
@@ -1,171 +1,169 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as need from '../lib/need'
3+import * as need from '../lib/need';
4−import * as azimuth from 'azimuth-js'
4+import * as azimuth from 'azimuth-js';
5−import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor, HorizontalSelector } from '../components/Base'
5+import {
6+ Row,
7+ Col,
8+ H1,
9+ P,
10+ InnerLabel,
11+ ShowBlockie,
12+ Anchor,
13+ HorizontalSelector,
14+} from '../components/Base';
6−import { AddressInput } from '../components/Base'
15+import { AddressInput } from '../components/Base';
7−import * as ob from 'urbit-ob'
16+import * as ob from 'urbit-ob';
8−
17+
9−import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
18+import { PROXY_TYPE, renderProxyType } from '../lib/proxy';
10−
19+
11−import { NETWORK_NAMES } from '../lib/network'
20+import { NETWORK_NAMES } from '../lib/network';
12−
21+
13−import StatelessTransaction from '../components/StatelessTransaction'
22+import StatelessTransaction from '../components/StatelessTransaction';
14−
23+
15−import { isValidAddress } from '../lib/wallet'
24+import { isValidAddress } from '../lib/wallet';
16−
25+
17−const SetManagementProxy = (props) =>
26+const SetManagementProxy = props => (
18− <SetProxy
19− { ...props }
20− proxyType={ PROXY_TYPE.MANAGEMENT_PROXY }
27+ <SetProxy {...props} proxyType={PROXY_TYPE.MANAGEMENT_PROXY} />
21− />
28+);
22−
29+
23−const SetSpawnProxy = (props) =>
30+const SetSpawnProxy = props => (
24− <SetProxy
25− { ...props }
26− proxyType={ PROXY_TYPE.SPAWN_PROXY }
31+ <SetProxy {...props} proxyType={PROXY_TYPE.SPAWN_PROXY} />
27− />
32+);
28−
33+
29−const SetTransferProxy = (props) =>
34+const SetTransferProxy = props => (
30− <SetProxy
31− { ...props }
32− proxyType={ PROXY_TYPE.TRANSFER_PROXY }
35+ <SetProxy {...props} proxyType={PROXY_TYPE.TRANSFER_PROXY} />
33− />
36+);
34
37
35class SetProxy extends React.Component {
38class SetProxy extends React.Component {
36 constructor(props) {
39 constructor(props) {
37− super(props)
40+ super(props);
38
41
39 const issuingPoint = need.pointCursor(props);
42 const issuingPoint = need.pointCursor(props);
40
43
41 this.state = {
44 this.state = {
42 proxyAddress: '',
45 proxyAddress: '',
43 issuingPoint: issuingPoint,
46 issuingPoint: issuingPoint,
44− setProxy: true
47+ setProxy: true,
45− }
48+ };
46
49
47 this.statelessRef = React.createRef();
50 this.statelessRef = React.createRef();
48 this.handleSetUnset = this.handleSetUnset.bind(this);
51 this.handleSetUnset = this.handleSetUnset.bind(this);
49− this.handleAddressInput = this.handleAddressInput.bind(this)
52+ this.handleAddressInput = this.handleAddressInput.bind(this);
50− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
53+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
51 }
54 }
52
55
53 handleSetUnset(selected) {
56 handleSetUnset(selected) {
54− const set = (selected === 'set');
57+ const set = selected === 'set';
55 this.setState({
58 this.setState({
56 setProxy: set,
59 setProxy: set,
57− proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000'
60+ proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000',
58 });
61 });
59 }
62 }
60
63
61 handleAddressInput(proxyAddress) {
64 handleAddressInput(proxyAddress) {
62− this.setState({ proxyAddress })
65+ this.setState({ proxyAddress });
63
66
64− this.statelessRef.current.clearTxn()
67+ this.statelessRef.current.clearTxn();
65 }
68 }
66
69
67 createUnsignedTxn(proxyAddress) {
70 createUnsignedTxn(proxyAddress) {
68− const { state, props } = this
71+ const { state, props } = this;
69
72
70 const validContracts = need.contracts(props);
73 const validContracts = need.contracts(props);
71
74
72 const validPoint = need.pointCursor(props);
75 const validPoint = need.pointCursor(props);
73
76
74− const txArgs = [
75− validContracts,
76− validPoint,
77− state.proxyAddress
77+ const txArgs = [validContracts, validPoint, state.proxyAddress];
78− ]
79
78
80 if (props.proxyType === PROXY_TYPE.TRANSFER_PROXY) {
79 if (props.proxyType === PROXY_TYPE.TRANSFER_PROXY) {
81− return Maybe.Just(azimuth.ecliptic.setTransferProxy(...txArgs))
80+ return Maybe.Just(azimuth.ecliptic.setTransferProxy(...txArgs));
82 }
81 }
83
82
84 if (props.proxyType === PROXY_TYPE.SPAWN_PROXY) {
83 if (props.proxyType === PROXY_TYPE.SPAWN_PROXY) {
85− return Maybe.Just(azimuth.ecliptic.setSpawnProxy(...txArgs))
84+ return Maybe.Just(azimuth.ecliptic.setSpawnProxy(...txArgs));
86 }
85 }
87
86
88 if (props.proxyType === PROXY_TYPE.MANAGEMENT_PROXY) {
87 if (props.proxyType === PROXY_TYPE.MANAGEMENT_PROXY) {
89− return Maybe.Just(azimuth.ecliptic.setManagementProxy(...txArgs))
88+ return Maybe.Just(azimuth.ecliptic.setManagementProxy(...txArgs));
90 }
89 }
91
90
92− return Maybe.Nothing()
91+ return Maybe.Nothing();
93−
94 }
92 }
95
93
96 render() {
94 render() {
97− const { props, state } = this
95+ const { props, state } = this;
98
96
99− const renderedProxyType = renderProxyType(props.proxyType)
97+ const renderedProxyType = renderProxyType(props.proxyType);
100− const validAddress = isValidAddress(state.proxyAddress)
98+ const validAddress = isValidAddress(state.proxyAddress);
101− const canGenerate = validAddress === true
99+ const canGenerate = validAddress === true;
102
100
103 const esvisible =
101 const esvisible =
104− props.networkType === NETWORK_NAMES.ROPSTEN ||
102+ props.networkType === NETWORK_NAMES.ROPSTEN ||
105− props.networkType === NETWORK_NAMES.MAINNET
103+ props.networkType === NETWORK_NAMES.MAINNET;
106
104
107 const esdomain =
105 const esdomain =
108− props.networkType === NETWORK_NAMES.ROPSTEN
106+ props.networkType === NETWORK_NAMES.ROPSTEN
109− ? "ropsten.etherscan.io"
107+ ? 'ropsten.etherscan.io'
110− : "etherscan.io"
108+ : 'etherscan.io';
111
109
112 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
110 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
113
111
114 const setUnset = [
112 const setUnset = [
115 { title: 'Set', value: 'set' },
113 { title: 'Set', value: 'set' },
116− { title: 'Unset', value: 'unset' }
114+ { title: 'Unset', value: 'unset' },
117 ];
115 ];
118
116
119 const titleVerb = this.state.setProxy ? 'Set' : 'Unset';
117 const titleVerb = this.state.setProxy ? 'Set' : 'Unset';
120
118
121 let addressInput = null;
119 let addressInput = null;
122 if (this.state.setProxy) {
120 if (this.state.setProxy) {
123− addressInput = (<>
121+ addressInput = (
122+ <>
124− <P className='mt-8'>
123+ <P className="mt-8">
125− {
126− 'Please provide an Ethereum address to act as the ' +
124+ {'Please provide an Ethereum address to act as the ' +
127− `${renderedProxyType} proxy. You can also use Wallet ` +
125+ `${renderedProxyType} proxy. You can also use Wallet ` +
128− 'Generator to generate a keypair.'
126+ 'Generator to generate a keypair.'}
129− }
130− </P>
127+ </P>
131−
128+
132− <AddressInput
129+ <AddressInput
133− className='mono mt-8'
130+ className="mono mt-8"
134− prop-size='lg'
131+ prop-size="lg"
135− prop-format='innerLabel'
132+ prop-format="innerLabel"
136− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
133+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
137− value={ state.proxyAddress }
134+ value={state.proxyAddress}
138− onChange={ v => this.handleAddressInput(v) }>
135+ onChange={v => this.handleAddressInput(v)}>
139− <InnerLabel>{ 'Proxy Address' }</InnerLabel>
136+ <InnerLabel>{'Proxy Address'}</InnerLabel>
140− <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
137+ <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
141− </AddressInput>
138+ </AddressInput>
142−
139+
143− <Anchor
140+ <Anchor
144− className={'mt-1'}
141+ className={'mt-1'}
145− prop-size={'sm'}
142+ prop-size={'sm'}
146− prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
143+ prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
147− target={'_blank'}
144+ target={'_blank'}
148− href={`https://${esdomain}/address/${state.proxyAddress}`}>
145+ href={`https://${esdomain}/address/${state.proxyAddress}`}>
149 {'View on Etherscan ↗'}
146 {'View on Etherscan ↗'}
150− </Anchor>
147+ </Anchor>
151− </>)
148+ </>
149+ );
152 }
150 }
153
151
154 return (
152 return (
155 <Row>
153 <Row>
156 <Col>
154 <Col>
157 <H1>
155 <H1>
158− { `${titleVerb} ${ucFirst(renderedProxyType)} Proxy For ` }
156+ {`${titleVerb} ${ucFirst(renderedProxyType)} Proxy For `}
159− <code>{ `${ob.patp(state.issuingPoint)}` }</code>
157+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
160 </H1>
158 </H1>
161
159
162 <HorizontalSelector
160 <HorizontalSelector
163 options={setUnset}
161 options={setUnset}
164 onChange={this.handleSetUnset}
162 onChange={this.handleSetUnset}
165− className='mt-8'
163+ className="mt-8"
166 />
164 />
167
165
168− { addressInput }
166+ {addressInput}
169
167
170 <StatelessTransaction
168 <StatelessTransaction
171 // Upper scope
169 // Upper scope
@@ -173,15 +171,12 @@ class SetProxy extends React.Component {
173 // Other
171 // Other
174 ref={this.statelessRef}
172 ref={this.statelessRef}
175 createUnsignedTxn={this.createUnsignedTxn}
173 createUnsignedTxn={this.createUnsignedTxn}
176− canGenerate={ canGenerate } />
174+ canGenerate={canGenerate}
175+ />
177 </Col>
176 </Col>
178 </Row>
177 </Row>
179− )
178+ );
180 }
179 }
181}
180}
182
181
183−export {
184− SetManagementProxy,
182+export { SetManagementProxy, SetSpawnProxy, SetTransferProxy };
185− SetSpawnProxy,
186− SetTransferProxy
187−}
src/bridge/views/Shards.js+111−117
@@ -1,86 +1,85 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Button } from '../components/Base'
3+import { Button } from '../components/Base';
4import {
4import {
5 InnerLabel,
5 InnerLabel,
6 ValidatedSigil,
6 ValidatedSigil,
7 PointInput,
7 PointInput,
8 ShardInput,
8 ShardInput,
9 InputCaption,
9 InputCaption,
10− Input
10+ Input,
11− } from '../components/Base'
11+} from '../components/Base';
12−import { Row, Col, H1, P } from '../components/Base'
12+import { Row, Col, H1, P } from '../components/Base';
13−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
13+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
14−import * as ob from 'urbit-ob'
14+import * as ob from 'urbit-ob';
15−
15+
16−import { ROUTE_NAMES } from '../lib/router'
16+import { ROUTE_NAMES } from '../lib/router';
17−import { urbitWalletFromTicket } from '../lib/wallet'
17+import { urbitWalletFromTicket } from '../lib/wallet';
18−
18+
19−const placeholder = (len) => {
19+const placeholder = len => {
20− let bytes = window.crypto.getRandomValues(new Uint8Array(len))
20+ let bytes = window.crypto.getRandomValues(new Uint8Array(len));
21− let hex = bytes.reduce((acc, byt) =>
21+ let hex = bytes.reduce(
22− acc + byt.toString(16).padStart(2, '0'),
22+ (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
23 ''
23 ''
24− )
24+ );
25− return ob.hex2patq(hex)
25+ return ob.hex2patq(hex);
26−}
26+};
27
27
28const SHARDS = {
28const SHARDS = {
29 SHARD1: Symbol('SHARD1'),
29 SHARD1: Symbol('SHARD1'),
30 SHARD2: Symbol('SHARD2'),
30 SHARD2: Symbol('SHARD2'),
31− SHARD3: Symbol('SHARD3')
31+ SHARD3: Symbol('SHARD3'),
32−}
32+};
33
33
34class Shards extends React.Component {
34class Shards extends React.Component {
35−
36 constructor(props) {
35 constructor(props) {
37− super(props)
36+ super(props);
38
37
39 this.state = {
38 this.state = {
40 shard1: '',
39 shard1: '',
41 shard2: '',
40 shard2: '',
42 shard3: '',
41 shard3: '',
43 passphrase: '',
42 passphrase: '',
44− pointName: ''
43+ pointName: '',
45− }
44+ };
46
45
47− this.pointPlaceholder = placeholder(4)
46+ this.pointPlaceholder = placeholder(4);
48− this.ticketPlaceholder = placeholder(16)
47+ this.ticketPlaceholder = placeholder(16);
49
48
50− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
49+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
51− this.handleShardInput = this.handleShardInput.bind(this)
50+ this.handleShardInput = this.handleShardInput.bind(this);
52− this.handlePointNameInput = this.handlePointNameInput.bind(this)
51+ this.handlePointNameInput = this.handlePointNameInput.bind(this);
53 }
52 }
54
53
55 handleShardInput(shard, input) {
54 handleShardInput(shard, input) {
56 if (shard === SHARDS.SHARD1) {
55 if (shard === SHARDS.SHARD1) {
57− this.setState({ shard1: input })
56+ this.setState({ shard1: input });
58 } else if (shard === SHARDS.SHARD2) {
57 } else if (shard === SHARDS.SHARD2) {
59− this.setState({ shard2: input })
58+ this.setState({ shard2: input });
60 } else if (shard === SHARDS.SHARD3) {
59 } else if (shard === SHARDS.SHARD3) {
61− this.setState({ shard3: input })
60+ this.setState({ shard3: input });
62 }
61 }
63 }
62 }
64
63
65 handlePassphraseInput(passphrase) {
64 handlePassphraseInput(passphrase) {
66− this.setState({ passphrase })
65+ this.setState({ passphrase });
67 }
66 }
68
67
69 handlePointNameInput(pointName) {
68 handlePointNameInput(pointName) {
70 if (pointName.length < 15) {
69 if (pointName.length < 15) {
71− this.setState({ pointName })
70+ this.setState({ pointName });
72 }
71 }
73 }
72 }
74
73
75 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
74 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
76− const s1 = shard1 === '' ? undefined : shard1
75+ const s1 = shard1 === '' ? undefined : shard1;
77− const s2 = shard2 === '' ? undefined : shard2
76+ const s2 = shard2 === '' ? undefined : shard2;
78− const s3 = shard3 === '' ? undefined : shard3
77+ const s3 = shard3 === '' ? undefined : shard3;
79
78
80− let ticket = undefined
79+ let ticket = undefined;
81 try {
80 try {
82− ticket = kg.combine([s1, s2, s3])
81+ ticket = kg.combine([s1, s2, s3]);
83− } catch(_) {
82+ } catch (_) {
84 // do nothing
83 // do nothing
85 }
84 }
86
85
@@ -91,101 +90,98 @@ class Shards extends React.Component {
91 }
90 }
92
91
93 render() {
92 render() {
94− const { popRoute, pushRoute, wallet } = this.props
93+ const { popRoute, pushRoute, wallet } = this.props;
95− const { shard1, shard2, shard3, pointName, passphrase } = this.state
94+ const { shard1, shard2, shard3, pointName, passphrase } = this.state;
96
95
97− const phPoint = this.pointPlaceholder
96+ const phPoint = this.pointPlaceholder;
98− const phTick = this.ticketPlaceholder
97+ const phTick = this.ticketPlaceholder;
99
98
100− const shards = [shard1, shard2, shard3]
99+ const shards = [shard1, shard2, shard3];
101− const ready = shards.filter(x => x !== '').length > 1
100+ const ready = shards.filter(x => x !== '').length > 1;
102
101
103 return (
102 return (
104− <Row>
103+ <Row>
105− <Col>
104+ <Col>
106− <H1>{ 'Authenticate' }</H1>
105+ <H1>{'Authenticate'}</H1>
107
106
108− <P>
107+ <P>
109− {
110− `Enter your point and at least two of your three Urbit master
108+ {`Enter your point and at least two of your three Urbit master
111 ticket shards here. The index of the input field needs to
109 ticket shards here. The index of the input field needs to
112− match the index of the shard.`
110+ match the index of the shard.`}
113− }
114−
115− </P>
111+ </P>
116
112
117 <PointInput
113 <PointInput
118− className='mono mt-8'
114+ className="mono mt-8"
119− prop-size='lg'
115+ prop-size="lg"
120− prop-format='innerLabel'
116+ prop-format="innerLabel"
121− type='text'
117+ type="text"
122 autoFocus
118 autoFocus
123− placeholder={ `e.g. ${phPoint}` }
119+ placeholder={`e.g. ${phPoint}`}
124− value={ pointName }
120+ value={pointName}
125− onChange={ this.handlePointNameInput }>
121+ onChange={this.handlePointNameInput}>
126− <InnerLabel>{ 'Point' }</InnerLabel>
122+ <InnerLabel>{'Point'}</InnerLabel>
127 <ValidatedSigil
123 <ValidatedSigil
128 className={'tr-0 mt-05 mr-0 abs'}
124 className={'tr-0 mt-05 mr-0 abs'}
129 patp={pointName}
125 patp={pointName}
130 size={68}
126 size={68}
131− margin={8} />
127+ margin={8}
128+ />
132− </PointInput>
129+ </PointInput>
133−
134
130
135 <ShardInput
131 <ShardInput
136− className='mono mt-8'
132+ className="mono mt-8"
137− prop-size='md'
133+ prop-size="md"
138− prop-format='innerLabel'
134+ prop-format="innerLabel"
139− type='text'
135+ type="text"
140− name='shard1'
136+ name="shard1"
141− placeholder={ `e.g. ${phTick}` }
137+ placeholder={`e.g. ${phTick}`}
142− value={ shard1 }
138+ value={shard1}
143− onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
139+ onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
144− <InnerLabel>{ 'Shard 1' }</InnerLabel>
140+ <InnerLabel>{'Shard 1'}</InnerLabel>
145 </ShardInput>
141 </ShardInput>
146
142
147 <ShardInput
143 <ShardInput
148− className='mono mt-8'
144+ className="mono mt-8"
149− prop-size='md'
145+ prop-size="md"
150− prop-format='innerLabel'
146+ prop-format="innerLabel"
151− type='text'
147+ type="text"
152− name='shard2'
148+ name="shard2"
153− placeholder={ `e.g. ${phTick}` }
149+ placeholder={`e.g. ${phTick}`}
154− value={ shard2 }
150+ value={shard2}
155− onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
151+ onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
156− <InnerLabel>{ 'Shard 2' }</InnerLabel>
152+ <InnerLabel>{'Shard 2'}</InnerLabel>
157 </ShardInput>
153 </ShardInput>
158
154
159 <ShardInput
155 <ShardInput
160− className='mono mt-8'
156+ className="mono mt-8"
161− prop-size='md'
157+ prop-size="md"
162− prop-format='innerLabel'
158+ prop-format="innerLabel"
163− type='text'
159+ type="text"
164− name='shard3'
160+ name="shard3"
165− placeholder={ `e.g. ${phTick}` }
161+ placeholder={`e.g. ${phTick}`}
166− value={ shard3 }
162+ value={shard3}
167− onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
163+ onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
168− <InnerLabel>{ 'Shard 3' }</InnerLabel>
164+ <InnerLabel>{'Shard 3'}</InnerLabel>
169 </ShardInput>
165 </ShardInput>
170
166
171 <InputCaption>
167 <InputCaption>
172− { 'If your wallet requires a passphrase, you may enter it below.' }
168+ {'If your wallet requires a passphrase, you may enter it below.'}
173 </InputCaption>
169 </InputCaption>
174
170
175 <Input
171 <Input
176− className='pt-8'
172+ className="pt-8"
177− prop-size='md'
173+ prop-size="md"
178− prop-format='innerLabel'
174+ prop-format="innerLabel"
179− name='passphrase'
175+ name="passphrase"
180− type='password'
176+ type="password"
181− value={ passphrase }
177+ value={passphrase}
182− autocomplete='off'
178+ autocomplete="off"
183− onChange={ this.handlePassphraseInput }>
179+ onChange={this.handlePassphraseInput}>
184 <InnerLabel>{'Passphrase'}</InnerLabel>
180 <InnerLabel>{'Passphrase'}</InnerLabel>
185 </Input>
181 </Input>
186
182
187 <Button
183 <Button
188− disabled={ !ready }
184+ disabled={!ready}
189 className={'mt-8'}
185 className={'mt-8'}
190 prop-size={'lg wide'}
186 prop-size={'lg wide'}
191 // prop-color={this.buttonTriState(wallet)}
187 // prop-color={this.buttonTriState(wallet)}
@@ -195,7 +191,8 @@ class Shards extends React.Component {
195 shard2,
191 shard2,
196 shard3,
192 shard3,
197 pointName,
193 pointName,
198− passphrase)
194+ passphrase
195+ )
199 }>
196 }>
200 {'Unlock Wallet →'}
197 {'Unlock Wallet →'}
201 </Button>
198 </Button>
@@ -203,20 +200,17 @@ class Shards extends React.Component {
203 <Button
200 <Button
204 className={'mt-4'}
201 className={'mt-4'}
205 prop-size={'xl wide'}
202 prop-size={'xl wide'}
206− disabled={ Nothing.hasInstance(wallet) }
203+ disabled={Nothing.hasInstance(wallet)}
207− onClick={ () => {
204+ onClick={() => {
208− popRoute()
205+ popRoute();
209− pushRoute(ROUTE_NAMES.SHIPS)
206+ pushRoute(ROUTE_NAMES.SHIPS);
210− }
211− }
207+ }}>
212− >
213− { 'Continue →' }
208+ {'Continue →'}
214 </Button>
209 </Button>
215−
216 </Col>
210 </Col>
217 </Row>
211 </Row>
218− )
212+ );
219 }
213 }
220}
214}
221
215
222−export default Shards
216+export default Shards;
src/bridge/views/Ticket.js+72−79
@@ -1,126 +1,125 @@
1−import { Just, Nothing } from 'folktale/maybe'
1+import { Just, Nothing } from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Button } from '../components/Base'
3+import { Button } from '../components/Base';
4import {
4import {
5 InnerLabel,
5 InnerLabel,
6 ValidatedSigil,
6 ValidatedSigil,
7 PointInput,
7 PointInput,
8 TicketInput,
8 TicketInput,
9 Input,
9 Input,
10− InputCaption
10+ InputCaption,
11− } from '../components/Base'
11+} from '../components/Base';
12−import { Row, Col, H1, P } from '../components/Base'
12+import { Row, Col, H1, P } from '../components/Base';
13
13
14−import { randomPatq } from '../lib/lib'
14+import { randomPatq } from '../lib/lib';
15−import { ROUTE_NAMES } from '../lib/router'
15+import { ROUTE_NAMES } from '../lib/router';
16−import { urbitWalletFromTicket } from '../lib/wallet'
16+import { urbitWalletFromTicket } from '../lib/wallet';
17
17
18class Ticket extends React.Component {
18class Ticket extends React.Component {
19−
20 constructor(props) {
19 constructor(props) {
21− super(props)
20+ super(props);
22
21
23 this.state = {
22 this.state = {
24 ticket: '',
23 ticket: '',
25 pointName: '',
24 pointName: '',
26 passphrase: '',
25 passphrase: '',
27− isUnlocking: false
26+ isUnlocking: false,
28− }
27+ };
29
28
30− this.pointPlaceholder = randomPatq(4)
29+ this.pointPlaceholder = randomPatq(4);
31− this.ticketPlaceholder = randomPatq(8)
30+ this.ticketPlaceholder = randomPatq(8);
32
31
33− this.handleTicketInput = this.handleTicketInput.bind(this)
32+ this.handleTicketInput = this.handleTicketInput.bind(this);
34− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
33+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
35− this.handlePointNameInput = this.handlePointNameInput.bind(this)
34+ this.handlePointNameInput = this.handlePointNameInput.bind(this);
36 }
35 }
37
36
38 handleTicketInput(ticket) {
37 handleTicketInput(ticket) {
39− this.setState({ ticket })
38+ this.setState({ ticket });
40 }
39 }
41
40
42 handlePassphraseInput(passphrase) {
41 handlePassphraseInput(passphrase) {
43− this.setState({ passphrase })
42+ this.setState({ passphrase });
44 }
43 }
45
44
46 handlePointNameInput(pointName) {
45 handlePointNameInput(pointName) {
47 if (pointName.length < 15) {
46 if (pointName.length < 15) {
48− this.setState({ pointName })
47+ this.setState({ pointName });
49 }
48 }
50 }
49 }
51
50
52 async walletFromTicket(ticket, pointName, passphrase) {
51 async walletFromTicket(ticket, pointName, passphrase) {
53 this.setState({
52 this.setState({
54− isUnlocking: true
53+ isUnlocking: true,
55 });
54 });
56
55
57 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
56 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
58 this.props.setUrbitWallet(Just(uhdw));
57 this.props.setUrbitWallet(Just(uhdw));
59
58
60 this.setState({
59 this.setState({
61− isUnlocking: false
60+ isUnlocking: false,
62 });
61 });
63 }
62 }
64
63
65 render() {
64 render() {
66− const { popRoute, pushRoute, wallet } = this.props
65+ const { popRoute, pushRoute, wallet } = this.props;
67− const { ticket, pointName, passphrase } = this.state
66+ const { ticket, pointName, passphrase } = this.state;
68
67
69− const phPoint = this.pointPlaceholder
68+ const phPoint = this.pointPlaceholder;
70− const phTick = this.ticketPlaceholder
69+ const phTick = this.ticketPlaceholder;
71
70
72 return (
71 return (
73− <Row>
72+ <Row>
74− <Col>
73+ <Col>
75− <H1>{ 'Authenticate' }</H1>
74+ <H1>{'Authenticate'}</H1>
76
75
77− <P>
76+ <P>
78− { `Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.` }
77+ {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
79− </P>
78+ </P>
80
79
81 <PointInput
80 <PointInput
82− className='mono mt-8'
81+ className="mono mt-8"
83− prop-size='lg'
82+ prop-size="lg"
84− prop-format='innerLabel'
83+ prop-format="innerLabel"
85− type='text'
84+ type="text"
86 autoFocus
85 autoFocus
87− placeholder={ `e.g. ${phPoint}` }
86+ placeholder={`e.g. ${phPoint}`}
88− value={ pointName }
87+ value={pointName}
89− onChange={ this.handlePointNameInput }>
88+ onChange={this.handlePointNameInput}>
90− <InnerLabel>{ 'Point' }</InnerLabel>
89+ <InnerLabel>{'Point'}</InnerLabel>
91 <ValidatedSigil
90 <ValidatedSigil
92 className={'tr-0 mt-05 mr-0 abs'}
91 className={'tr-0 mt-05 mr-0 abs'}
93 patp={pointName}
92 patp={pointName}
94 size={68}
93 size={68}
95− margin={8} />
94+ margin={8}
95+ />
96− </PointInput>
96+ </PointInput>
97−
98
97
99 <TicketInput
98 <TicketInput
100− className='mono mt-8'
99+ className="mono mt-8"
101− prop-size='lg'
100+ prop-size="lg"
102− prop-format='innerLabel'
101+ prop-format="innerLabel"
103− type='password'
102+ type="password"
104− name='ticket'
103+ name="ticket"
105− placeholder={ `e.g. ${phTick}` }
104+ placeholder={`e.g. ${phTick}`}
106− value={ ticket }
105+ value={ticket}
107− onChange={ this.handleTicketInput }>
106+ onChange={this.handleTicketInput}>
108− <InnerLabel>{ 'Ticket' }</InnerLabel>
107+ <InnerLabel>{'Ticket'}</InnerLabel>
109 </TicketInput>
108 </TicketInput>
110
109
111 <InputCaption>
110 <InputCaption>
112− { 'If your wallet requires a passphrase, you may enter it below.' }
111+ {'If your wallet requires a passphrase, you may enter it below.'}
113 </InputCaption>
112 </InputCaption>
114
113
115 <Input
114 <Input
116− className='pt-8'
115+ className="pt-8"
117− prop-size='md'
116+ prop-size="md"
118− prop-format='innerLabel'
117+ prop-format="innerLabel"
119− name='passphrase'
118+ name="passphrase"
120− type='password'
119+ type="password"
121− value={ passphrase }
120+ value={passphrase}
122− autocomplete='off'
121+ autocomplete="off"
123− onChange={ this.handlePassphraseInput }>
122+ onChange={this.handlePassphraseInput}>
124 <InnerLabel>{'Passphrase'}</InnerLabel>
123 <InnerLabel>{'Passphrase'}</InnerLabel>
125 </Input>
124 </Input>
126
125
@@ -128,14 +127,11 @@ class Ticket extends React.Component {
128 className={'mt-8'}
127 className={'mt-8'}
129 prop-size={'lg wide'}
128 prop-size={'lg wide'}
130 disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
129 disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
131− onClick={
130+ onClick={() =>
132− () => this.walletFromTicket(ticket, pointName, passphrase)
131+ this.walletFromTicket(ticket, pointName, passphrase)
133 }>
132 }>
134−
135 <span className="relative">
133 <span className="relative">
136− {this.state.isUnlocking &&
137− <span className="btn-spinner"></span>
134+ {this.state.isUnlocking && <span className="btn-spinner"></span>}
138− }
139 {'Unlock Wallet →'}
135 {'Unlock Wallet →'}
140 </span>
136 </span>
141 </Button>
137 </Button>
@@ -143,20 +139,17 @@ class Ticket extends React.Component {
143 <Button
139 <Button
144 className={'mt-4'}
140 className={'mt-4'}
145 prop-size={'xl wide'}
141 prop-size={'xl wide'}
146− disabled={ Nothing.hasInstance(wallet) }
142+ disabled={Nothing.hasInstance(wallet)}
147− onClick={ () => {
143+ onClick={() => {
148− popRoute()
144+ popRoute();
149− pushRoute(ROUTE_NAMES.SHIPS)
145+ pushRoute(ROUTE_NAMES.SHIPS);
150− }
151− }
146+ }}>
152− >
153− { 'Continue →' }
147+ {'Continue →'}
154 </Button>
148 </Button>
155−
156 </Col>
149 </Col>
157 </Row>
150 </Row>
158− )
151+ );
159 }
152 }
160}
153}
161
154
162−export default Ticket
155+export default Ticket;
src/bridge/views/Transfer.js+56−49
@@ -1,48 +1,54 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as azimuth from 'azimuth-js'
3+import * as azimuth from 'azimuth-js';
4−import * as ob from 'urbit-ob'
4+import * as ob from 'urbit-ob';
5−import * as need from '../lib/need'
5+import * as need from '../lib/need';
6
6
7+import {
8+ Row,
9+ Col,
10+ H1,
11+ InnerLabel,
12+ ShowBlockie,
13+ P,
14+ Anchor,
7−import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
15+} from '../components/Base';
8−import { AddressInput } from '../components/Base'
16+import { AddressInput } from '../components/Base';
9−import StatelessTransaction from '../components/StatelessTransaction'
17+import StatelessTransaction from '../components/StatelessTransaction';
10
18
11−import { NETWORK_NAMES } from '../lib/network'
19+import { NETWORK_NAMES } from '../lib/network';
12
20
13−import {
14− isValidAddress
21+import { isValidAddress } from '../lib/wallet';
15−} from '../lib/wallet'
16
22
17class Transfer extends React.Component {
23class Transfer extends React.Component {
18 constructor(props) {
24 constructor(props) {
19− super(props)
25+ super(props);
20
26
21 const issuingPoint = need.pointCursor(props);
27 const issuingPoint = need.pointCursor(props);
22
28
23 this.state = {
29 this.state = {
24 receivingAddress: '',
30 receivingAddress: '',
25 issuingPoint: issuingPoint,
31 issuingPoint: issuingPoint,
26− }
32+ };
27
33
28− this.handleAddressInput = this.handleAddressInput.bind(this)
34+ this.handleAddressInput = this.handleAddressInput.bind(this);
29− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
35+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
30 this.statelessRef = React.createRef();
36 this.statelessRef = React.createRef();
31 }
37 }
32
38
33 handleAddressInput(receivingAddress) {
39 handleAddressInput(receivingAddress) {
34− this.setState({ receivingAddress })
40+ this.setState({ receivingAddress });
35− this.statelessRef.current.clearTxn()
41+ this.statelessRef.current.clearTxn();
36 }
42 }
37
43
38 handleConfirmAvailability() {
44 handleConfirmAvailability() {
39 this.confirmPointAvailability().then(r => {
45 this.confirmPointAvailability().then(r => {
40− this.setState({ isAvailable: r })
46+ this.setState({ isAvailable: r });
41− })
47+ });
42 }
48 }
43
49
44 createUnsignedTxn() {
50 createUnsignedTxn() {
45− const { state, props } = this
51+ const { state, props } = this;
46
52
47 const validContracts = need.contracts(props);
53 const validContracts = need.contracts(props);
48
54
@@ -52,60 +58,60 @@ class Transfer extends React.Component {
52 validContracts,
58 validContracts,
53 validPoint,
59 validPoint,
54 state.receivingAddress
60 state.receivingAddress
55− )
61+ );
56
62
57− return Maybe.Just(txn)
63+ return Maybe.Just(txn);
58 }
64 }
59
65
60 render() {
66 render() {
61− const { props, state } = this
67+ const { props, state } = this;
62
68
63− const validAddress = isValidAddress(state.receivingAddress)
69+ const validAddress = isValidAddress(state.receivingAddress);
64− const canGenerate = validAddress === true
70+ const canGenerate = validAddress === true;
65
71
66 const esvisible =
72 const esvisible =
67− props.networkType === NETWORK_NAMES.ROPSTEN ||
73+ props.networkType === NETWORK_NAMES.ROPSTEN ||
68− props.networkType === NETWORK_NAMES.MAINNET
74+ props.networkType === NETWORK_NAMES.MAINNET;
69
75
70 const esdomain =
76 const esdomain =
71− props.networkType === NETWORK_NAMES.ROPSTEN
77+ props.networkType === NETWORK_NAMES.ROPSTEN
72− ? "ropsten.etherscan.io"
78+ ? 'ropsten.etherscan.io'
73− : "etherscan.io"
79+ : 'etherscan.io';
74
80
75 return (
81 return (
76 <Row>
82 <Row>
77 <Col>
83 <Col>
78 <H1>
84 <H1>
79− { 'Transfer'} <code>{ ` ${ob.patp(state.issuingPoint)} ` }</code>
85+ {'Transfer'} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
80− { 'To a New Owner' }
86+ {'To a New Owner'}
81 </H1>
87 </H1>
82
88
83 <P>
89 <P>
84− {
90+ {`Please provide the Ethereum address of the new owner. You own these
85− `Please provide the Ethereum address of the new owner. You own these
86 points until the recipient accepts the incoming transfer.
91 points until the recipient accepts the incoming transfer.
87− You may cancel the transfer until the transfer is accepted.`
92+ You may cancel the transfer until the transfer is accepted.`}
88− }
89 </P>
93 </P>
90
94
91 <AddressInput
95 <AddressInput
92 className={'mono mt-8'}
96 className={'mono mt-8'}
93− prop-size='lg'
97+ prop-size="lg"
94− prop-format='innerLabel'
98+ prop-format="innerLabel"
95− value={ state.receivingAddress }
99+ value={state.receivingAddress}
96− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
100+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
97− onChange={ v => this.handleAddressInput(v) }>
101+ onChange={v => this.handleAddressInput(v)}>
98− <InnerLabel>{ 'New ownership address' }</InnerLabel>
102+ <InnerLabel>{'New ownership address'}</InnerLabel>
99 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
103 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
100 </AddressInput>
104 </AddressInput>
101
105
102 <Anchor
106 <Anchor
103 className={'mt-1'}
107 className={'mt-1'}
104 prop-size={'s'}
108 prop-size={'s'}
105− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
109+ prop-disabled={
110+ !isValidAddress(state.receivingAddress) || !esvisible
111+ }
106 target={'_blank'}
112 target={'_blank'}
107 href={`https://${esdomain}/address/${state.receivingAddress}`}>
113 href={`https://${esdomain}/address/${state.receivingAddress}`}>
108− {'View on Etherscan ↗'}
114+ {'View on Etherscan ↗'}
109 </Anchor>
115 </Anchor>
110
116
111 <StatelessTransaction
117 <StatelessTransaction
@@ -113,14 +119,15 @@ class Transfer extends React.Component {
113 {...props}
119 {...props}
114 // Checks
120 // Checks
115 userApproval={state.userApproval}
121 userApproval={state.userApproval}
116− canGenerate={ canGenerate }
122+ canGenerate={canGenerate}
117 // Methods
123 // Methods
118 createUnsignedTxn={this.createUnsignedTxn}
124 createUnsignedTxn={this.createUnsignedTxn}
119− ref={this.statelessRef} />
125+ ref={this.statelessRef}
126+ />
120 </Col>
127 </Col>
121 </Row>
128 </Row>
122− )
129+ );
123 }
130 }
124}
131}
125
132
126−export default Transfer
133+export default Transfer;
src/bridge/views/Trezor.js+89−95
@@ -1,28 +1,27 @@
1−import * as bip32 from 'bip32'
1+import * as bip32 from 'bip32';
2−import React from 'react'
2+import React from 'react';
3−import Maybe from 'folktale/maybe'
3+import Maybe from 'folktale/maybe';
4−import { Button } from '../components/Base'
4+import { Button } from '../components/Base';
5−import { Row, Col, H1, P } from '../components/Base'
5+import { Row, Col, H1, P } from '../components/Base';
6−import { InnerLabel, Input, InnerLabelDropdown } from '../components/Base'
6+import { InnerLabel, Input, InnerLabelDropdown } from '../components/Base';
7−import TrezorConnect from 'trezor-connect'
7+import TrezorConnect from 'trezor-connect';
8−import * as secp256k1 from 'secp256k1'
8+import * as secp256k1 from 'secp256k1';
9−
9+
10−import { TREZOR_PATH } from '../lib/trezor'
10+import { TREZOR_PATH } from '../lib/trezor';
11−import { ROUTE_NAMES } from '../lib/router'
11+import { ROUTE_NAMES } from '../lib/router';
12
12
13class Trezor extends React.Component {
13class Trezor extends React.Component {
14−
15 constructor(props) {
14 constructor(props) {
16− super(props)
15+ super(props);
17
16
18 this.state = {
17 this.state = {
19 hdpath: TREZOR_PATH.replace(/x/g, 0),
18 hdpath: TREZOR_PATH.replace(/x/g, 0),
20− account: 0
19+ account: 0,
21− }
20+ };
22
21
23− this.handleAccountSelection = this.handleAccountSelection.bind(this)
22+ this.handleAccountSelection = this.handleAccountSelection.bind(this);
24− this.handleHdPathInput = this.handleHdPathInput.bind(this)
23+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
25− this.pollDevice = this.pollDevice.bind(this)
24+ this.pollDevice = this.pollDevice.bind(this);
26 }
25 }
27
26
28 handleAccountSelection(account) {
27 handleAccountSelection(account) {
@@ -34,116 +33,111 @@ class Trezor extends React.Component {
34 }
33 }
35
34
36 handleHdPathInput(hdpath) {
35 handleHdPathInput(hdpath) {
37− this.setState({ hdpath })
36+ this.setState({ hdpath });
38 }
37 }
39
38
40 async pollDevice() {
39 async pollDevice() {
41− const { setWallet, setWalletHdPath } = this.props
40+ const { setWallet, setWalletHdPath } = this.props;
42− const { hdpath } = this.state
41+ const { hdpath } = this.state;
43
42
44 TrezorConnect.manifest({
43 TrezorConnect.manifest({
45 email: 'bridge-trezor@urbit.org',
44 email: 'bridge-trezor@urbit.org',
46− appUrl: 'https://github.com/urbit/bridge'
45+ appUrl: 'https://github.com/urbit/bridge',
46+ });
47+ TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
48+ if (info.success === true) {
49+ const payload = info.payload;
50+ const publicKey = Buffer.from(payload.publicKey, 'hex');
51+ const chainCode = Buffer.from(payload.chainCode, 'hex');
52+ const pub = secp256k1.publicKeyConvert(publicKey, true);
53+ const hd = bip32.fromPublicKey(pub, chainCode);
54+ setWallet(Maybe.Just(hd));
55+ setWalletHdPath(hdpath);
56+ } else {
57+ setWallet(Maybe.Nothing());
58+ }
47 });
59 });
48− TrezorConnect.getPublicKey({ path: hdpath })
49− .then(info => {
50− if (info.success === true) {
51− const payload = info.payload
52− const publicKey = Buffer.from(payload.publicKey, 'hex')
53− const chainCode = Buffer.from(payload.chainCode, 'hex')
54− const pub = secp256k1.publicKeyConvert(publicKey, true)
55− const hd = bip32.fromPublicKey(pub, chainCode)
56− setWallet(Maybe.Just(hd))
57− setWalletHdPath(hdpath)
58− } else {
59− setWallet(Maybe.Nothing())
60− }
61− })
62 }
60 }
63
61
64 render() {
62 render() {
65− const { pushRoute, popRoute, wallet } = this.props
63+ const { pushRoute, popRoute, wallet } = this.props;
66− const { hdpath, account } = this.state
64+ const { hdpath, account } = this.state;
67 const { handleAccountSelection } = this;
65 const { handleAccountSelection } = this;
68
66
69− let accountOptions = [{
67+ let accountOptions = [
70− title: 'Custom path',
68+ {
71− value: 'custom'
69+ title: 'Custom path',
72− }];
70+ value: 'custom',
71+ },
72+ ];
73 for (let i = 0; i < 20; i++) {
73 for (let i = 0; i < 20; i++) {
74 accountOptions.push({
74 accountOptions.push({
75− title: 'Account #' + (i+1),
75+ title: 'Account #' + (i + 1),
76− value: i
76+ value: i,
77 });
77 });
78 }
78 }
79− let accountTitle = accountOptions.find(o => (o.value === account)).title;
79+ let accountTitle = accountOptions.find(o => o.value === account).title;
80
80
81 const accountSelection = (
81 const accountSelection = (
82 <InnerLabelDropdown
82 <InnerLabelDropdown
83− className='mt-8'
83+ className="mt-8"
84− prop-size='md'
84+ prop-size="md"
85− prop-format='innerLabel'
85+ prop-format="innerLabel"
86 options={accountOptions}
86 options={accountOptions}
87 handleUpdate={handleAccountSelection}
87 handleUpdate={handleAccountSelection}
88 title={'Account'}
88 title={'Account'}
89 currentSelectionTitle={accountTitle}
89 currentSelectionTitle={accountTitle}
90− fullWidth={true}
90+ fullWidth={true}></InnerLabelDropdown>
91− >
92− </InnerLabelDropdown>
93 );
91 );
94
92
95− const pathSelection = (account !== 'custom') ? null :
93+ const pathSelection =
96− (
94+ account !== 'custom' ? null : (
97 <Input
95 <Input
98− className='mt-8 pt-8 text-mono'
96+ className="mt-8 pt-8 text-mono"
99− prop-size='md'
97+ prop-size="md"
100− prop-format='innerLabel'
98+ prop-format="innerLabel"
101− name='hdpath'
99+ name="hdpath"
102− value={ hdpath }
100+ value={hdpath}
103− autocomplete='off'
101+ autocomplete="off"
104− onChange={ this.handleHdPathInput }>
102+ onChange={this.handleHdPathInput}>
105 <InnerLabel>{'HD Path'}</InnerLabel>
103 <InnerLabel>{'HD Path'}</InnerLabel>
106 </Input>
104 </Input>
107 );
105 );
108
106
109 return (
107 return (
110− <Row>
108+ <Row>
111− <Col className={'measure-md'}>
109+ <Col className={'measure-md'}>
112− <H1>{ 'Authenticate With Your Trezor' }</H1>
110+ <H1>{'Authenticate With Your Trezor'}</H1>
113−
111+
114− <P>
112+ <P>
115− { `Connect and authenticate to your Trezor. If you'd like
113+ {`Connect and authenticate to your Trezor. If you'd like
116− to use a custom derivation path, you may enter it below.`
114+ to use a custom derivation path, you may enter it below.`}
117− }
115+ </P>
118− </P>
116+
119−
117+ {accountSelection}
120− { accountSelection }
118+ {pathSelection}
121− { pathSelection }
119+
122−
120+ <Button
123− <Button
121+ className={'mt-8'}
124− className={'mt-8'}
122+ prop-size={'wide lg'}
125− prop-size={'wide lg'}
123+ onClick={this.pollDevice}>
126− onClick={ this.pollDevice }>
124+ {'Authenticate →'}
127− { 'Authenticate →' }
125+ </Button>
128− </Button>
126+
129−
127+ <Button
130− <Button
128+ className={'mt-8'}
131− className={'mt-8'}
129+ prop-size={'wide lg'}
132− prop-size={'wide lg'}
130+ disabled={Maybe.Nothing.hasInstance(wallet)}
133− disabled={ Maybe.Nothing.hasInstance(wallet) }
131+ onClick={() => {
134− onClick={
132+ popRoute();
135− () => {
133+ pushRoute(ROUTE_NAMES.SHIPS);
136− popRoute()
134+ }}>
137− pushRoute(ROUTE_NAMES.SHIPS)
135+ {'Continue →'}
138− }
136+ </Button>
139− }>
140− { 'Continue →' }
141− </Button>
142−
143 </Col>
137 </Col>
144 </Row>
138 </Row>
145− )
139+ );
146 }
140 }
147}
141}
148
142
149−export default Trezor
143+export default Trezor;
src/bridge/views/ViewPoint.js+38−43
@@ -1,60 +1,58 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import * as ob from 'urbit-ob'
3+import * as ob from 'urbit-ob';
4
4
5−import { Button } from '../components/Base'
5+import { Button } from '../components/Base';
6−import { Row, Col, H1, P } from '../components/Base'
6+import { Row, Col, H1, P } from '../components/Base';
7−import { InnerLabel, PointInput, ValidatedSigil } from '../components/Base'
7+import { InnerLabel, PointInput, ValidatedSigil } from '../components/Base';
8
8
9−import { ROUTE_NAMES } from '../lib/router'
9+import { ROUTE_NAMES } from '../lib/router';
10
10
11class ViewPoint extends React.Component {
11class ViewPoint extends React.Component {
12 constructor(props) {
12 constructor(props) {
13− super(props)
13+ super(props);
14
14
15 this.state = {
15 this.state = {
16− pointName: ''
16+ pointName: '',
17− }
17+ };
18
18
19− this.handlePointInput = this.handlePointInput.bind(this)
19+ this.handlePointInput = this.handlePointInput.bind(this);
20 }
20 }
21
21
22− handlePointInput = (pointName) => {
22+ handlePointInput = pointName => {
23 if (pointName.length < 15) {
23 if (pointName.length < 15) {
24− this.setState({ pointName })
24+ this.setState({ pointName });
25 }
25 }
26− }
26+ };
27
27
28 render() {
28 render() {
29− const { pointName } = this.state
29+ const { pointName } = this.state;
30− const { popRoute, pushRoute, setPointCursor } = this.props
30+ const { popRoute, pushRoute, setPointCursor } = this.props;
31
31
32 // NB (jtobin):
32 // NB (jtobin):
33 //
33 //
34 // could use a better patp parser in urbit-ob
34 // could use a better patp parser in urbit-ob
35− let valid
35+ let valid;
36 try {
36 try {
37− valid = ob.isValidPatp(pointName)
37+ valid = ob.isValidPatp(pointName);
38− } catch(err) {
38+ } catch (err) {
39− valid = false
39+ valid = false;
40 }
40 }
41
41
42 return (
42 return (
43 <Row>
43 <Row>
44 <Col>
44 <Col>
45− <H1>{ 'View a Point' }</H1>
45+ <H1>{'View a Point'}</H1>
46
46
47− <P>
47+ <P>{'Enter a point name to view its public information.'}</P>
48− { "Enter a point name to view its public information." }
49− </P>
50
48
51 <PointInput
49 <PointInput
52 autoFocus
50 autoFocus
53− prop-size='lg'
51+ prop-size="lg"
54− prop-format='innerLabel'
52+ prop-format="innerLabel"
55− className='mono'
53+ className="mono"
56− placeholder='e.g. ~zod'
54+ placeholder="e.g. ~zod"
57− onChange={ this.handlePointInput }
55+ onChange={this.handlePointInput}
58 value={pointName}>
56 value={pointName}>
59 <InnerLabel>{'Point Name'}</InnerLabel>
57 <InnerLabel>{'Point Name'}</InnerLabel>
60 <ValidatedSigil
58 <ValidatedSigil
@@ -62,27 +60,24 @@ class ViewPoint extends React.Component {
62 patp={pointName}
60 patp={pointName}
63 show
61 show
64 size={68}
62 size={68}
65− margin={8} />
63+ margin={8}
64+ />
66 </PointInput>
65 </PointInput>
67
66
68 <Button
67 <Button
69 className={'mt-8'}
68 className={'mt-8'}
70− disabled={ valid === false }
69+ disabled={valid === false}
71− onClick={
70+ onClick={() => {
72− () => {
71+ setPointCursor(Maybe.Just(ob.patp2dec(pointName)));
73− setPointCursor(Maybe.Just(ob.patp2dec(pointName)))
72+ popRoute();
74− popRoute()
73+ pushRoute(ROUTE_NAMES.SHIP);
75− pushRoute(ROUTE_NAMES.SHIP)
74+ }}>
76− }
75+ {'Continue →'}
77− }
78− >
79− { 'Continue →' }
80 </Button>
76 </Button>
81 </Col>
77 </Col>
82 </Row>
78 </Row>
83− )
79+ );
84 }
80 }
85−
86}
81}
87
82
88−export default ViewPoint
83+export default ViewPoint;
src/bridge/views/Wallet.js+76−70
@@ -1,101 +1,107 @@
1−import Maybe from 'folktale/maybe'
1+import Maybe from 'folktale/maybe';
2−import React from 'react'
2+import React from 'react';
3−import { Button, H1, P } from '../components/Base'
3+import { Button, H1, P } from '../components/Base';
4−import { InnerLabelDropdown } from '../components/Base'
4+import { InnerLabelDropdown } from '../components/Base';
5−import { Row, Col } from '../components/Base'
5+import { Row, Col } from '../components/Base';
6
6
7−import { ROUTE_NAMES } from '../lib/router'
7+import { ROUTE_NAMES } from '../lib/router';
8−import { WALLET_NAMES, renderWalletType } from '../lib/wallet'
8+import { WALLET_NAMES, renderWalletType } from '../lib/wallet';
9
9
10class Wallet extends React.Component {
10class Wallet extends React.Component {
11 componentDidMount() {
11 componentDidMount() {
12− const { setWallet } = this.props
12+ const { setWallet } = this.props;
13− setWallet(Maybe.Nothing())
13+ setWallet(Maybe.Nothing());
14 }
14 }
15
15
16 getWalletOptions() {
16 getWalletOptions() {
17− return [{
17+ return [
18− title: 'Urbit Master Ticket',
18+ {
19− value: WALLET_NAMES.TICKET
19+ title: 'Urbit Master Ticket',
20− }, {
20+ value: WALLET_NAMES.TICKET,
21− title: 'Urbit Master Ticket (Shards)',
21+ },
22− value: WALLET_NAMES.SHARDS
22+ {
23− }, {
23+ title: 'Urbit Master Ticket (Shards)',
24− type: 'divider'
24+ value: WALLET_NAMES.SHARDS,
25− }, {
25+ },
26− title: 'Ledger',
26+ {
27− value: WALLET_NAMES.LEDGER
27+ type: 'divider',
28− }, {
28+ },
29− title: 'Trezor',
29+ {
30− value: WALLET_NAMES.TREZOR
30+ title: 'Ledger',
31− }, {
31+ value: WALLET_NAMES.LEDGER,
32− type: 'divider'
32+ },
33− }, {
33+ {
34− title: 'BIP39 Mnemonic',
34+ title: 'Trezor',
35− value: WALLET_NAMES.MNEMONIC
35+ value: WALLET_NAMES.TREZOR,
36− }, {
36+ },
37− title: 'Ethereum Private Key',
37+ {
38− value: WALLET_NAMES.PRIVATE_KEY
38+ type: 'divider',
39− }, {
39+ },
40− title: 'Ethereum Keystore File',
40+ {
41− value: WALLET_NAMES.KEYSTORE
41+ title: 'BIP39 Mnemonic',
42− }]
42+ value: WALLET_NAMES.MNEMONIC,
43+ },
44+ {
45+ title: 'Ethereum Private Key',
46+ value: WALLET_NAMES.PRIVATE_KEY,
47+ },
48+ {
49+ title: 'Ethereum Keystore File',
50+ value: WALLET_NAMES.KEYSTORE,
51+ },
52+ ];
43 }
53 }
44
54
45 render() {
55 render() {
46− const { props } = this
56+ const { props } = this;
47− const walletOptions = this.getWalletOptions()
57+ const walletOptions = this.getWalletOptions();
48
58
49 return (
59 return (
50 <Row>
60 <Row>
51 <Col>
61 <Col>
52− <H1>{ 'Unlock a Wallet' }</H1>
62+ <H1>{'Unlock a Wallet'}</H1>
53
63
54− <P>
64+ <P>
55− {
65+ {`To manage your assets, you need to unlock a wallet.
56− `To manage your assets, you need to unlock a wallet.
66+ Please select how you would like to access your wallet.`}
57− Please select how you would like to access your wallet.`
67+ </P>
58− }
59−
60− </P>
61
68
62 <InnerLabelDropdown
69 <InnerLabelDropdown
63 className={'mb-10 mt-6'}
70 className={'mb-10 mt-6'}
64 title={'Wallet Type:'}
71 title={'Wallet Type:'}
65 options={walletOptions}
72 options={walletOptions}
66 handleUpdate={props.setWalletType}
73 handleUpdate={props.setWalletType}
67− currentSelectionTitle={renderWalletType(props.walletType)}>
74+ currentSelectionTitle={renderWalletType(
68− </InnerLabelDropdown>
75+ props.walletType
76+ )}></InnerLabelDropdown>
69
77
70 <Button
78 <Button
71 className={'mt-10'}
79 className={'mt-10'}
72− onClick={
80+ onClick={() =>
73− () => props.pushRoute(
81+ props.pushRoute(
74− props.walletType === WALLET_NAMES.MNEMONIC
82+ props.walletType === WALLET_NAMES.MNEMONIC
75− ? ROUTE_NAMES.MNEMONIC
83+ ? ROUTE_NAMES.MNEMONIC
76− : props.walletType === WALLET_NAMES.TICKET
84+ : props.walletType === WALLET_NAMES.TICKET
77− ? ROUTE_NAMES.TICKET
85+ ? ROUTE_NAMES.TICKET
78− : props.walletType === WALLET_NAMES.SHARDS
86+ : props.walletType === WALLET_NAMES.SHARDS
79− ? ROUTE_NAMES.SHARDS
87+ ? ROUTE_NAMES.SHARDS
80− : props.walletType === WALLET_NAMES.LEDGER
88+ : props.walletType === WALLET_NAMES.LEDGER
81− ? ROUTE_NAMES.LEDGER
89+ ? ROUTE_NAMES.LEDGER
82− : props.walletType === WALLET_NAMES.TREZOR
90+ : props.walletType === WALLET_NAMES.TREZOR
83− ? ROUTE_NAMES.TREZOR
91+ ? ROUTE_NAMES.TREZOR
84− : props.walletType === WALLET_NAMES.PRIVATE_KEY
92+ : props.walletType === WALLET_NAMES.PRIVATE_KEY
85− ? ROUTE_NAMES.PRIVATE_KEY
93+ ? ROUTE_NAMES.PRIVATE_KEY
86− : props.walletType === WALLET_NAMES.KEYSTORE
94+ : props.walletType === WALLET_NAMES.KEYSTORE
87− ? ROUTE_NAMES.KEYSTORE
95+ ? ROUTE_NAMES.KEYSTORE
88− : ROUTE_NAMES.DEFAULT
96+ : ROUTE_NAMES.DEFAULT
89 )
97 )
90− }
98+ }>
91− >
99+ {'Continue →'}
92− { 'Continue →' }
93 </Button>
100 </Button>
94 </Col>
101 </Col>
95 </Row>
102 </Row>
96− )
103+ );
97 }
104 }
98−
99}
105}
100
106
101−export default Wallet
107+export default Wallet;
src/common/components/AppNavigation.js+16−20
@@ -2,42 +2,38 @@ import React from 'react';
2
2
3class AppNavigation extends React.Component {
3class AppNavigation extends React.Component {
4 constructor(props) {
4 constructor(props) {
5− super(props)
5+ super(props);
6
6
7− this.navigate = this.navigate.bind(this)
7+ this.navigate = this.navigate.bind(this);
8 }
8 }
9
9
10 navigate() {
10 navigate() {
11 let appView;
11 let appView;
12− if (this.props.appView === "bridge") appView = "walletgen"
12+ if (this.props.appView === 'bridge') appView = 'walletgen';
13− if (this.props.appView === "walletgen") appView = "bridge"
13+ if (this.props.appView === 'walletgen') appView = 'bridge';
14
14
15− this.props.setView(appView)
15+ this.props.setView(appView);
16 }
16 }
17
17
18 render() {
18 render() {
19 let buttonText;
19 let buttonText;
20− if (this.props.appView === "bridge") buttonText = "Go to Walletgen"
20+ if (this.props.appView === 'bridge') buttonText = 'Go to Walletgen';
21− if (this.props.appView === "walletgen") buttonText = "Go to Bridge"
21+ if (this.props.appView === 'walletgen') buttonText = 'Go to Bridge';
22
22
23 let style = {
23 let style = {
24− position: "fixed",
24+ position: 'fixed',
25− bottom: "10px",
25+ bottom: '10px',
26− right: "10px",
26+ right: '10px',
27− textDecoration: "underline",
27+ textDecoration: 'underline',
28− cursor: "pointer"
28+ cursor: 'pointer',
29− }
29+ };
30
30
31 return (
31 return (
32− <span
32+ <span style={style} onClick={this.navigate}>
33− style={style}
34− onClick={this.navigate}>
35 {buttonText}
33 {buttonText}
36 </span>
34 </span>
37− )
35+ );
38 }
36 }
39}
37}
40
38
41−export {
39+export { AppNavigation };
42− AppNavigation
43−}
src/index.js+5−8
@@ -1,11 +1,8 @@
1−import 'babel-polyfill' // required for @ledgerhq/hw-transport-u2f
1+import 'babel-polyfill'; // required for @ledgerhq/hw-transport-u2f
2
2
3−import React from 'react'
3+import React from 'react';
4−import ReactDOM from 'react-dom'
4+import ReactDOM from 'react-dom';
5
5
6−import { Root } from './Root'
6+import { Root } from './Root';
7
7
8−ReactDOM.render(
8+ReactDOM.render(<Root />, document.getElementById('root'));
9− <Root />,
10− document.getElementById('root')
11−)
src/walletgen/Walletgen.js+64−86
@@ -1,68 +1,47 @@
1−import React from 'react'
1+import React from 'react';
2−import * as lodash from 'lodash'
2+import * as lodash from 'lodash';
3
3
4−import Header from './components/Header'
4+import Header from './components/Header';
5−import Footer from './components/Footer'
5+import Footer from './components/Footer';
6
6
7−import { NETWORK_STATES, PROFILE_STATES, GEN_STATES } from './lib/constants'
7+import { NETWORK_STATES, PROFILE_STATES, GEN_STATES } from './lib/constants';
8
8
9−import PrivacyPolicy from './views/PrivacyPolicy'
9+import PrivacyPolicy from './views/PrivacyPolicy';
10−import Welcome from './views/Welcome'
10+import Welcome from './views/Welcome';
11−import Eula from './views/Eula'
11+import Eula from './views/Eula';
12−import EulaView from './views/EulaView'
12+import EulaView from './views/EulaView';
13−import Upload from './views/Upload'
13+import Upload from './views/Upload';
14−import Understand from './views/Understand'
14+import Understand from './views/Understand';
15−import Generate from './views/Generate'
15+import Generate from './views/Generate';
16−import Custody from './views/Custody'
16+import Custody from './views/Custody';
17−import Download from './views/Download'
17+import Download from './views/Download';
18−import Done from './views/Done'
18+import Done from './views/Done';
19
19
20import './style/index.css';
20import './style/index.css';
21
21
22−const checkNetwork = () => navigator.onLine
22+const checkNetwork = () =>
23− ? NETWORK_STATES.ONLINE
23+ navigator.onLine ? NETWORK_STATES.ONLINE : NETWORK_STATES.OFFLINE;
24− : NETWORK_STATES.OFFLINE
25
24
26const ROUTES = [
25const ROUTES = [
27− { path: '/Welcome',
26+ { path: '/Welcome', Component: Welcome },
28− Component: Welcome,
27+ { path: '/Eula', Component: Eula },
29− },
28+ { path: '/Upload', Component: Upload },
30− { path: '/Eula',
29+ { path: '/Understand', Component: Understand },
31− Component: Eula,
30+ { path: '/Custody', Component: Custody },
32− },
31+ { path: '/Generate', Component: Generate },
33− { path: '/Upload',
32+ { path: '/Download', Component: Download },
34− Component: Upload,
33+ { path: '/Done', Component: Done },
35− },
34+ { path: '/PrivacyPolicy', Component: PrivacyPolicy },
36− { path: '/Understand',
35+ { path: '/EulaView', Component: EulaView },
37− Component: Understand,
38− },
39− { path: '/Custody',
40− Component: Custody,
41− },
42− { path: '/Generate',
43− Component: Generate,
44− },
45− { path: '/Download',
46− Component: Download,
47− },
48− { path: '/Done',
49− Component: Done,
50− },
51− { path: '/PrivacyPolicy',
52− Component: PrivacyPolicy,
53− },
54− { path: '/EulaView',
55− Component: EulaView,
56− }
57];
36];
58
37
59const TOTAL_STEPS = 7;
38const TOTAL_STEPS = 7;
60
39
61class Walletgen extends React.Component {
40class Walletgen extends React.Component {
62 constructor(props) {
41 constructor(props) {
63− super(props)
42+ super(props);
64 this.state = {
43 this.state = {
65− 'network': checkNetwork(),
44+ network: checkNetwork(),
66 'profile.state': PROFILE_STATES.NOSTART,
45 'profile.state': PROFILE_STATES.NOSTART,
67 'profile.value': null,
46 'profile.value': null,
68 'profile.shipCount': 0,
47 'profile.shipCount': 0,
@@ -77,73 +56,72 @@ class Walletgen extends React.Component {
77 'paperCollateral.state': GEN_STATES.PAPER_NOSTART,
56 'paperCollateral.state': GEN_STATES.PAPER_NOSTART,
78 'paperCollateral.value': null,
57 'paperCollateral.value': null,
79 'paperCollateral.error': '',
58 'paperCollateral.error': '',
80− 'route': '/Welcome',
59+ route: '/Welcome',
81− 'lastRoute': '/Welcome',
60+ lastRoute: '/Welcome',
82− 'currentStep': 1,
61+ currentStep: 1,
83− 'totalSteps': TOTAL_STEPS,
62+ totalSteps: TOTAL_STEPS,
84− 'downloaded': false,
63+ downloaded: false,
85− 'didClickDownload': false,
64+ didClickDownload: false,
86 };
65 };
87− };
66+ }
88−
89
67
90 componentDidMount = () => {
68 componentDidMount = () => {
91− window.addEventListener('online', () => this.setState({'network': NETWORK_STATES.ONLINE}));
69+ window.addEventListener('online', () =>
92− window.addEventListener('offline', () => this.setState({'network': NETWORK_STATES.OFFLINE}));
70+ this.setState({ network: NETWORK_STATES.ONLINE })
71+ );
72+ window.addEventListener('offline', () =>
73+ this.setState({ network: NETWORK_STATES.OFFLINE })
74+ );
93 };
75 };
94
76
95−
77+ setGlobalState = state => this.setState(state);
96− setGlobalState = state => this.setState(state)
97
78
98 router = (network, path) => {
79 router = (network, path) => {
99 const route = key => {
80 const route = key => {
100− const { Component } = lodash.find(ROUTES, { path: key })
81+ const { Component } = lodash.find(ROUTES, { path: key });
101 const globals = {
82 const globals = {
102 setGlobalState: this.setGlobalState,
83 setGlobalState: this.setGlobalState,
103− ...this.state
84+ ...this.state,
104− }
85+ };
105− return <Component { ...globals } />
86+ return <Component {...globals} />;
106− }
87+ };
107
88
108− return route(path)
89+ return route(path);
109
90
110 // return network === NETWORK_STATES.ONLINE
91 // return network === NETWORK_STATES.ONLINE
111 // ? <NetworkWarning />
92 // ? <NetworkWarning />
112 // : !(network === NETWORK_STATES.OFFLINE)
93 // : !(network === NETWORK_STATES.OFFLINE)
113 // ? <h1>Detecting your network state.</h1>
94 // ? <h1>Detecting your network state.</h1>
114 // : route(path)
95 // : route(path)
115− }
96+ };
116−
117
97
118 render() {
98 render() {
119 const { network, route, currentStep, lastRoute } = this.state;
99 const { network, route, currentStep, lastRoute } = this.state;
120
100
121− const routed = this.router(network, route) // hacky prod
101+ const routed = this.router(network, route); // hacky prod
122 // const routed = this.router(NETWORK_STATES.OFFLINE, true, route) // hacky dev
102 // const routed = this.router(NETWORK_STATES.OFFLINE, true, route) // hacky dev
123
103
124 return (
104 return (
125 <main className={'container'}>
105 <main className={'container'}>
126−
127 <Header
106 <Header
128− network={ network }
107+ network={network}
129− currentStep={ currentStep }
108+ currentStep={currentStep}
130− totalSteps={ TOTAL_STEPS } />
109+ totalSteps={TOTAL_STEPS}
131−
110+ />
132− <div className={ 'row wrapper' }>
111+
133− <div className={'col-'}>
112+ <div className={'row wrapper'}>
134− { routed }
113+ <div className={'col-'}>{routed}</div>
135− </div>
136 <div className={'push'} />
114 <div className={'push'} />
137 </div>
115 </div>
138
116
139 <Footer
117 <Footer
140− route={ route }
118+ route={route}
141− lastRoute={ lastRoute }
119+ lastRoute={lastRoute}
142− setGlobalState={ this.setGlobalState }
120+ setGlobalState={this.setGlobalState}
143 />
121 />
144 </main>
122 </main>
145 );
123 );
146− };
124+ }
147−};
125+}
148
126
149−export default Walletgen
127+export default Walletgen;
src/walletgen/components/Button.js+5−9
@@ -1,13 +1,9 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−const Button = ({ disabled, onClick, text, className }) =>
3+const Button = ({ disabled, onClick, text, className }) => (
4− <button
4+ <button className={className} onClick={onClick} disabled={disabled}>
5− className={className}
6− onClick={onClick}
7− disabled={disabled}
8− >
9 {text}
5 {text}
10 </button>
6 </button>
7+);
11
8
12−export default Button
9+export default Button;
13−
src/walletgen/components/CheckBox.js+22−12
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
2
2
3const CheckBox = ({
3const CheckBox = ({
4 name,
4 name,
@@ -9,7 +9,7 @@ const CheckBox = ({
9 className,
9 className,
10 state,
10 state,
11}) => {
11}) => {
12− const isCheckedClassname = state === true ? 'isChecked' : 'isNotChecked'
12+ const isCheckedClassname = state === true ? 'isChecked' : 'isNotChecked';
13 return (
13 return (
14 <div className={`checkbox flex ${isCheckedClassname} ${className}`}>
14 <div className={`checkbox flex ${isCheckedClassname} ${className}`}>
15 <label>
15 <label>
@@ -19,20 +19,30 @@ const CheckBox = ({
19 type={'checkbox'}
19 type={'checkbox'}
20 className={`rc-checkbox-input`}
20 className={`rc-checkbox-input`}
21 checked={state}
21 checked={state}
22− onChange={onChange} />
22+ onChange={onChange}
23+ />
23
24
24− <svg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'>
25+ <svg
25− <path d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z' transform='translate(3.84906 11.3371) rotate(-45)' fill='white'/>
26+ width="20"
27+ height="20"
28+ viewBox="0 0 20 20"
29+ fill="none"
30+ xmlns="http://www.w3.org/2000/svg">
31+ <path
32+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
33+ transform="translate(3.84906 11.3371) rotate(-45)"
34+ fill="white"
35+ />
26 </svg>
36 </svg>
27−
28 </label>
37 </label>
29 <div>
38 <div>
30− <p onChange={onChange} checked={state}>{title}</p>
39+ <p onChange={onChange} checked={state}>
31− { subtitle === '' ? null : <p>{subtitle}</p> }
40+ {title}
41+ </p>
42+ {subtitle === '' ? null : <p>{subtitle}</p>}
32 </div>
43 </div>
33 </div>
44 </div>
34− )
45+ );
35−}
46+};
36−
37
47
38−export default CheckBox
48+export default CheckBox;
src/walletgen/components/EulaText.js+434−68
@@ -1,146 +1,512 @@
1−import React from 'react'
1+import React from 'react';
2
2
3// EULA
3// EULA
4−const EulaText = () =>
4+const EulaText = () => (
5 <span className={'legal'}>
5 <span className={'legal'}>
6 <h2>Terms of Use</h2>
6 <h2>Terms of Use</h2>
7
7
8− <p>This agreement (these <b>“Terms”</b>) governs the terms under which you may access and use the Wallet Generator software (the <b>“Software”</b>) made available to you by Tlon Corporation (“Tlon”, <b>“we”</b> or <b>“us”</b>). By clicking “I Accept,” or by downloading, installing, or otherwise accessing or using the Software, you agree that you have read and understood, and, as a condition to your use of the Software, you agree to be bound by, the following terms and conditions.
8+ <p>
9+ This agreement (these <b>“Terms”</b>) governs the terms under which you
10+ may access and use the Wallet Generator software (the <b>“Software”</b>)
11+ made available to you by Tlon Corporation (“Tlon”, <b>“we”</b> or{' '}
12+ <b>“us”</b>). By clicking “I Accept,” or by downloading, installing, or
13+ otherwise accessing or using the Software, you agree that you have read
14+ and understood, and, as a condition to your use of the Software, you agree
15+ to be bound by, the following terms and conditions.
9 </p>
16 </p>
10
17
11 <p>
18 <p>
12− <b>BY CLICKING “I ACCEPT,” OR BY DOWNLOADING, INSTALLING, OR OTHERWISE ACCESSING OR USING THE SOFTWARE</b>, YOU AGREE THAT YOU HAVE READ AND UNDERSTOOD, AND, AS A CONDITION TO YOUR USE OF THE SOFTWARE, YOU AGREE TO BE BOUND BY, THE FOLLOWING TERMS AND CONDITIONS (THESE “<b>TERMS</b>”). IF YOU ARE NOT ELIGIBLE, OR DO NOT AGREE TO THE TERMS, THEN YOU DO NOT HAVE OUR PERMISSION TO USE THE SOFTWARE. YOUR USE OF THE SOFTWARE, AND TLON’S PROVISION OF THE SOFTWARE TO YOU, CONSTITUTES AN AGREEMENT BY TLON AND BY YOU TO BE BOUND BY THESE TERMS.
19+ <b>
20+ BY CLICKING “I ACCEPT,” OR BY DOWNLOADING, INSTALLING, OR OTHERWISE
21+ ACCESSING OR USING THE SOFTWARE
22+ </b>
23+ , YOU AGREE THAT YOU HAVE READ AND UNDERSTOOD, AND, AS A CONDITION TO YOUR
24+ USE OF THE SOFTWARE, YOU AGREE TO BE BOUND BY, THE FOLLOWING TERMS AND
25+ CONDITIONS (THESE “<b>TERMS</b>”). IF YOU ARE NOT ELIGIBLE, OR DO NOT
26+ AGREE TO THE TERMS, THEN YOU DO NOT HAVE OUR PERMISSION TO USE THE
27+ SOFTWARE. YOUR USE OF THE SOFTWARE, AND TLON’S PROVISION OF THE SOFTWARE
28+ TO YOU, CONSTITUTES AN AGREEMENT BY TLON AND BY YOU TO BE BOUND BY THESE
29+ TERMS.
13 </p>
30 </p>
14
31
15 <p>
32 <p>
16− <b><u>MATERIAL TERMS</u>: AS PROVIDED IN GREATER DETAIL IN THESE TERMS (AND WITHOUT LIMITING THE EXPRESS LANGUAGE OF THESE TERMS), YOU ACKNOWLEDGE THE FOLLOWING:</b>
33+ <b>
34+ <u>MATERIAL TERMS</u>: AS PROVIDED IN GREATER DETAIL IN THESE TERMS (AND
35+ WITHOUT LIMITING THE EXPRESS LANGUAGE OF THESE TERMS), YOU ACKNOWLEDGE
36+ THE FOLLOWING:
37+ </b>
17 </p>
38 </p>
18
39
19 <ul>
40 <ul>
20− <li><p><b>
41+ <li>
21− WE DO NOT HOLD, BACK-UP OTHERWISE HAVE ANY ACCESS TO YOUR KEYS (DEFINED IN SECTION 1), SO YOU ARE SOLELY RESPONSIBLE FOR ANY LOSS OF THE SAME, AND YOU ARE SOLELY RESPONSIBLE FOR MAINTAINING ALL INFORMATION NECESSARY TO ACCESS YOUR KEYS AND WALLET;
42+ <p>
22− </b></p></li>
43+ <b>
23− <li><p><b>
44+ WE DO NOT HOLD, BACK-UP OTHERWISE HAVE ANY ACCESS TO YOUR KEYS
24− WE HAVE NO LIABILITY WHATSOEVER FOR ANY SECURITY PROBLEMS OR INCIDENTS THAT YOU MAY EXPERIENCE IN CONNECTION WITH ANY USE OF THE SOFTWARE, INCLUDING ANY LOSS OR THEFT OF YOUR KEYS OR ANY PROBLEMS THAT MAY ARISE IN CONNECTION WITH YOUR WALLET;
45+ (DEFINED IN SECTION 1), SO YOU ARE SOLELY RESPONSIBLE FOR ANY LOSS
25− </b></p></li>
46+ OF THE SAME, AND YOU ARE SOLELY RESPONSIBLE FOR MAINTAINING ALL
26− <li><p><b>
47+ INFORMATION NECESSARY TO ACCESS YOUR KEYS AND WALLET;
27− THE SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND AND OUR LIABILITY TO YOU IN CONNECTION WITH THE SAME IS LIMITED; AND
48+ </b>
28− </b></p></li>
49+ </p>
29− <li><p><b>
50+ </li>
30− ALL DISPUTES ARISING UNDER THESE TERMS WILL BE RESOLVED THROUGH BINDING ARBITRATION AND YOU AND TLON ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
51+ <li>
31− </b></p></li>
52+ <p>
53+ <b>
54+ WE HAVE NO LIABILITY WHATSOEVER FOR ANY SECURITY PROBLEMS OR
55+ INCIDENTS THAT YOU MAY EXPERIENCE IN CONNECTION WITH ANY USE OF THE
56+ SOFTWARE, INCLUDING ANY LOSS OR THEFT OF YOUR KEYS OR ANY PROBLEMS
57+ THAT MAY ARISE IN CONNECTION WITH YOUR WALLET;
58+ </b>
59+ </p>
60+ </li>
61+ <li>
62+ <p>
63+ <b>
64+ THE SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND AND
65+ OUR LIABILITY TO YOU IN CONNECTION WITH THE SAME IS LIMITED; AND
66+ </b>
67+ </p>
68+ </li>
69+ <li>
70+ <p>
71+ <b>
72+ ALL DISPUTES ARISING UNDER THESE TERMS WILL BE RESOLVED THROUGH
73+ BINDING ARBITRATION AND YOU AND TLON ARE EACH WAIVING THE RIGHT TO A
74+ TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
75+ </b>
76+ </p>
77+ </li>
32 </ul>
78 </ul>
33
79
34 <ol>
80 <ol>
35 <li>
81 <li>
36− <p><b>Software Overview.</b> The Software is an interface that you can use to generate a hierarchical deterministic wallet that enables the creation of child keys from parent keys in a hierarchy (“Wallet”). Those keys can be private keys or can involve a public Ethereum Wallet address that can be used to send and receive Urbit address space in transactions with third parties (such private keys and public addresses, the “Keys”). The Software may only be used if you are disconnected from the internet and will automatically close if it detects an internet connection. Tlon recommends that you use the Software only on an airgapped machine.</p>
82+ <p>
83+ <b>Software Overview.</b> The Software is an interface that you can
84+ use to generate a hierarchical deterministic wallet that enables the
85+ creation of child keys from parent keys in a hierarchy (“Wallet”).
86+ Those keys can be private keys or can involve a public Ethereum Wallet
87+ address that can be used to send and receive Urbit address space in
88+ transactions with third parties (such private keys and public
89+ addresses, the “Keys”). The Software may only be used if you are
90+ disconnected from the internet and will automatically close if it
91+ detects an internet connection. Tlon recommends that you use the
92+ Software only on an airgapped machine.
93+ </p>
37 </li>
94 </li>
38 <li>
95 <li>
39− <p><b>Eligibility.</b> You must be at least 18 years old to use the Software. By agreeing to these Terms, you represent and warrant to us that: (a) you are at least 18 years old; (b) your registration and your use of the Software is in compliance with any and all applicable laws and regulations; and (c) that all information you submit in connection with your download or use of the Software is truthful and accurate.
96+ <p>
97+ <b>Eligibility.</b> You must be at least 18 years old to use the
98+ Software. By agreeing to these Terms, you represent and warrant to us
99+ that: (a) you are at least 18 years old; (b) your registration and
100+ your use of the Software is in compliance with any and all applicable
101+ laws and regulations; and (c) that all information you submit in
102+ connection with your download or use of the Software is truthful and
103+ accurate.
40 </p>
104 </p>
41 </li>
105 </li>
42 <li>
106 <li>
43− <p><b>License.</b></p>
107+ <p>
108+ <b>License.</b>
109+ </p>
44 <ol>
110 <ol>
45 <li>
111 <li>
46− <p><b>Limited License.</b> Subject to your complete and ongoing compliance with these Terms, Tlon grants you, solely for your personal use, a limited, non-exclusive, non-transferable, non-sublicensable, revocable license to install and use one object code copy of the Software.
112+ <p>
113+ <b>Limited License.</b> Subject to your complete and ongoing
114+ compliance with these Terms, Tlon grants you, solely for your
115+ personal use, a limited, non-exclusive, non-transferable,
116+ non-sublicensable, revocable license to install and use one object
117+ code copy of the Software.
47 </p>
118 </p>
48 </li>
119 </li>
49 <li>
120 <li>
50− <p><b>License Restrictions.</b> Except and solely to the extent such a restriction is impermissible under applicable law, you may not: (a) reproduce, distribute, publicly display, or publicly perform the Software; (b) make modifications to the Software; or (c) interfere with or circumvent any feature of the Software, including any security or access control mechanism. If you are prohibited under applicable law from using the Software, you may not use it.
121+ <p>
122+ <b>License Restrictions.</b> Except and solely to the extent such
123+ a restriction is impermissible under applicable law, you may not:
124+ (a) reproduce, distribute, publicly display, or publicly perform
125+ the Software; (b) make modifications to the Software; or (c)
126+ interfere with or circumvent any feature of the Software,
127+ including any security or access control mechanism. If you are
128+ prohibited under applicable law from using the Software, you may
129+ not use it.
51 </p>
130 </p>
52 </li>
131 </li>
53 <li>
132 <li>
54− <p><b>Feedback.</b> If you choose to provide input and suggestions regarding problems with or proposed modifications or improvements to the Software (“<b>Feedback</b>”), then you hereby grant Tlon an unrestricted, perpetual, irrevocable, non-exclusive, fully-paid, royalty-free right to exploit the Feedback in any manner and for any purpose, including to improve the Software and create other products and services.
133+ <p>
134+ <b>Feedback.</b> If you choose to provide input and suggestions
135+ regarding problems with or proposed modifications or improvements
136+ to the Software (“<b>Feedback</b>”), then you hereby grant Tlon an
137+ unrestricted, perpetual, irrevocable, non-exclusive, fully-paid,
138+ royalty-free right to exploit the Feedback in any manner and for
139+ any purpose, including to improve the Software and create other
140+ products and services.
55 </p>
141 </p>
56 </li>
142 </li>
57 </ol>
143 </ol>
58 </li>
144 </li>
59
145
60 <li>
146 <li>
61− <p><b>Ownership; Proprietary Rights.</b> The Software is owned and operated by Tlon. The visual interfaces, graphics, design, compilation, information, data, computer code (including source code or object code), products, software, services, and all other elements of the Software (“<b>Materials</b>”) provided by Tlon are protected by intellectual property and other laws. All Materials included in the Software are the property of Tlon or its third party licensors. Except as expressly authorized by Tlon, you may not make use of the Materials. Tlon reserves all rights to the Materials not granted expressly in these Terms.
147+ <p>
148+ <b>Ownership; Proprietary Rights.</b> The Software is owned and
149+ operated by Tlon. The visual interfaces, graphics, design,
150+ compilation, information, data, computer code (including source code
151+ or object code), products, software, services, and all other elements
152+ of the Software (“<b>Materials</b>”) provided by Tlon are protected by
153+ intellectual property and other laws. All Materials included in the
154+ Software are the property of Tlon or its third party licensors. Except
155+ as expressly authorized by Tlon, you may not make use of the
156+ Materials. Tlon reserves all rights to the Materials not granted
157+ expressly in these Terms.
62 </p>
158 </p>
63 </li>
159 </li>
64
160
65 <li>
161 <li>
66− <p><b>Third Party Software.</b> The Software may include or incorporate third party software components that are generally available free of charge under licenses granting recipients broad rights to copy, modify, and distribute those components (“<b>Third Party Components</b>”). Although the Software is provided to you subject to these Terms, nothing in these Terms prevents, restricts, or is intended to prevent or restrict you from obtaining Third Party Components under the applicable third party licenses or to limit your use of Third Party Components under those third party licenses.
162+ <p>
163+ <b>Third Party Software.</b> The Software may include or incorporate
164+ third party software components that are generally available free of
165+ charge under licenses granting recipients broad rights to copy,
166+ modify, and distribute those components (“
167+ <b>Third Party Components</b>”). Although the Software is provided to
168+ you subject to these Terms, nothing in these Terms prevents,
169+ restricts, or is intended to prevent or restrict you from obtaining
170+ Third Party Components under the applicable third party licenses or to
171+ limit your use of Third Party Components under those third party
172+ licenses.
67 </p>
173 </p>
68 </li>
174 </li>
69
175
70 <li>
176 <li>
71− <p><b>Prohibited Conduct.</b> BY USING THE SOFTWARE YOU AGREE NOT TO, AND AGREE NOT TO ATTEMPT TO OR PERMIT ANY OTHER PERSON TO:
177+ <p>
178+ <b>Prohibited Conduct.</b> BY USING THE SOFTWARE YOU AGREE NOT TO, AND
179+ AGREE NOT TO ATTEMPT TO OR PERMIT ANY OTHER PERSON TO:
72 </p>
180 </p>
73 <ol className={'ls-alpha'}>
181 <ol className={'ls-alpha'}>
74− <li><p>use the Software for any illegal purpose or in violation of any local, state, national, or international law;
182+ <li>
75− </p></li>
183+ <p>
76− <li><p>interfere with security-related features of the Software, including by: (i) disabling or circumventing features that prevent or limit use or copying of any content; or (ii) reverse engineering or otherwise attempting to discover the source code of any portion of the Software except to the extent that the activity is expressly permitted by applicable law, or except to the extent that Tlon has made such source code available to the public;
184+ use the Software for any illegal purpose or in violation of any
77− </p></li>
185+ local, state, national, or international law;
78− <li><p>perform any fraudulent activity including impersonating any person or entity, claiming a false affiliation, accessing any other Software account without permission; or
186+ </p>
79− </p></li>
187+ </li>
80− <li><p>sell or otherwise transfer the access granted under these Terms or any Materials (as defined in Section 4) or any right or ability to view, access, or use any Materials.
188+ <li>
81− </p></li>
189+ <p>
190+ interfere with security-related features of the Software,
191+ including by: (i) disabling or circumventing features that prevent
192+ or limit use or copying of any content; or (ii) reverse
193+ engineering or otherwise attempting to discover the source code of
194+ any portion of the Software except to the extent that the activity
195+ is expressly permitted by applicable law, or except to the extent
196+ that Tlon has made such source code available to the public;
197+ </p>
198+ </li>
199+ <li>
200+ <p>
201+ perform any fraudulent activity including impersonating any person
202+ or entity, claiming a false affiliation, accessing any other
203+ Software account without permission; or
204+ </p>
205+ </li>
206+ <li>
207+ <p>
208+ sell or otherwise transfer the access granted under these Terms or
209+ any Materials (as defined in Section 4) or any right or ability to
210+ view, access, or use any Materials.
211+ </p>
212+ </li>
82 </ol>
213 </ol>
83 </li>
214 </li>
84
215
85 <li>
216 <li>
86− <p><b>Term, Termination and Modification of the Software.</b> These Terms are effective beginning when you accept the Terms or first download, install, access, or use the Software, and ending when terminated as described in this Section. If you violate any provision of these Terms, your authorization to access the Software and these Terms automatically terminate. In addition, Tlon may, at its sole discretion, terminate these Terms or suspend or terminate your right to access to the Software, at any time for any reason or no reason, with or without notice. Tlon reserves the right to modify or discontinue the Software at any time (including by limiting or discontinuing certain features of the Software), temporarily or permanently, without notice to you. Tlon will have no liability for any change to the Software or any suspension or termination of your access to or use of the Software. Upon any termination of these Terms, you must permanently delete all copies of the Software within your possession or control, and Sections 3.3, 4, 8, 9, 10, 11, and 12 will survive.
217+ <p>
218+ <b>Term, Termination and Modification of the Software.</b> These Terms
219+ are effective beginning when you accept the Terms or first download,
220+ install, access, or use the Software, and ending when terminated as
221+ described in this Section. If you violate any provision of these
222+ Terms, your authorization to access the Software and these Terms
223+ automatically terminate. In addition, Tlon may, at its sole
224+ discretion, terminate these Terms or suspend or terminate your right
225+ to access to the Software, at any time for any reason or no reason,
226+ with or without notice. Tlon reserves the right to modify or
227+ discontinue the Software at any time (including by limiting or
228+ discontinuing certain features of the Software), temporarily or
229+ permanently, without notice to you. Tlon will have no liability for
230+ any change to the Software or any suspension or termination of your
231+ access to or use of the Software. Upon any termination of these Terms,
232+ you must permanently delete all copies of the Software within your
233+ possession or control, and Sections 3.3, 4, 8, 9, 10, 11, and 12 will
234+ survive.
87 </p>
235 </p>
88 </li>
236 </li>
89
237
90 <li>
238 <li>
91− <p><b>Indemnity.</b> To the fullest extent permitted by law, you are responsible for your use of the Software, and you will defend and indemnify Tlon and its officers, directors, employees, consultants, affiliates, subsidiaries and agents (together, the “Tlon Entities”) from and against every claim brought by a third party, and any related liability, damage, loss, and expense, including reasonable attorneys’ fees and costs, arising out of or connected with: (a) your use of, or misuse of, the Software; and (b) your violation of any portion of these Terms, any representation, warranty, or any applicable law or regulation. We reserve the right, at our own expense, to assume the exclusive defense and control of any matter otherwise subject to indemnification by you (without limiting your indemnification obligations with respect to that matter), and in that case, you agree to cooperate with our defense of those claims.
239+ <p>
240+ <b>Indemnity.</b> To the fullest extent permitted by law, you are
241+ responsible for your use of the Software, and you will defend and
242+ indemnify Tlon and its officers, directors, employees, consultants,
243+ affiliates, subsidiaries and agents (together, the “Tlon Entities”)
244+ from and against every claim brought by a third party, and any related
245+ liability, damage, loss, and expense, including reasonable attorneys’
246+ fees and costs, arising out of or connected with: (a) your use of, or
247+ misuse of, the Software; and (b) your violation of any portion of
248+ these Terms, any representation, warranty, or any applicable law or
249+ regulation. We reserve the right, at our own expense, to assume the
250+ exclusive defense and control of any matter otherwise subject to
251+ indemnification by you (without limiting your indemnification
252+ obligations with respect to that matter), and in that case, you agree
253+ to cooperate with our defense of those claims.
92 </p>
254 </p>
93 </li>
255 </li>
94
256
95 <li>
257 <li>
96− <p><b>Disclaimers; No Warranties.</b> </p>
258+ <p>
97− <p>THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE SOFTWARE ARE PROVIDED “AS IS” AND ON AN “AS AVAILABLE” BASIS. TLON DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, RELATING TO THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE SOFTWARE, INCLUDING: (A) ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, QUIET ENJOYMENT, OR NON-INFRINGEMENT; AND (B) ANY WARRANTY ARISING OUT OF COURSE OF DEALING, USAGE, OR TRADE. TLON DOES NOT WARRANT THAT THE SOFTWARE OR ANY PORTION OF THE SOFTWARE, OR ANY MATERIALS OR CONTENT OFFERED THROUGH THE SOFTWARE, WILL BE UNINTERRUPTED, SECURE, OR FREE OF ERRORS, VIRUSES, OR OTHER HARMFUL COMPONENTS, AND TLON DOES NOT WARRANT THAT ANY OF THOSE ISSUES WILL BE CORRECTED.
259+ <b>Disclaimers; No Warranties.</b>{' '}
98 </p>
260 </p>
99− <p>NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU FROM THE SOFTWARE OR TLON ENTITIES OR ANY MATERIALS OR CONTENT AVAILABLE THROUGH THE SOFTWARE WILL CREATE ANY WARRANTY REGARDING ANY OF THE TLON ENTITIES OR THE SOFTWARE THAT IS NOT EXPRESSLY STATED IN THESE TERMS. WE ARE NOT RESPONSIBLE FOR ANY DAMAGE THAT MAY RESULT FROM THE SOFTWARE.
261+ <p>
262+ THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE
263+ SOFTWARE ARE PROVIDED “AS IS” AND ON AN “AS AVAILABLE” BASIS. TLON
264+ DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED,
265+ RELATING TO THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE
266+ THROUGH THE SOFTWARE, INCLUDING: (A) ANY IMPLIED WARRANTY OF
267+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, QUIET
268+ ENJOYMENT, OR NON-INFRINGEMENT; AND (B) ANY WARRANTY ARISING OUT OF
269+ COURSE OF DEALING, USAGE, OR TRADE. TLON DOES NOT WARRANT THAT THE
270+ SOFTWARE OR ANY PORTION OF THE SOFTWARE, OR ANY MATERIALS OR CONTENT
271+ OFFERED THROUGH THE SOFTWARE, WILL BE UNINTERRUPTED, SECURE, OR FREE
272+ OF ERRORS, VIRUSES, OR OTHER HARMFUL COMPONENTS, AND TLON DOES NOT
273+ WARRANT THAT ANY OF THOSE ISSUES WILL BE CORRECTED.
100 </p>
274 </p>
101− <p><b>YOU ARE SOLELY RESPONSIBLE FOR RECORDING AND SAFEGUARDING YOUR KEYS AND ANY INFORMATION THAT YOU MAY NEED TO RECOVER THE SAME. YOU UNDERSTAND AND AGREE THAT YOU USE ANY PORTION OF THE SOFTWARE AT YOUR OWN DISCRETION AND RISK, AND THAT WE ARE NOT RESPONSIBLE FOR ANY DAMAGE TO YOUR PROPERTY (INCLUDING YOUR COMPUTER SYSTEM OR MOBILE DEVICE USED IN CONNECTION WITH THE SOFTWARE) OR ANY LOSS OF DATA OR OTHER PROPERTY OR ASSETS YOU MAY OWN OR CONTROL IN CONNECTION WITH YOUR USE OF THE SOFTWARE, KEYS OR WALLET, INCLUDING ANY LOSS OF ACCESS TO YOUR WALLET OR KEYS.
275+ <p>
102− </b></p>
276+ NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU
103− <p>THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS IN THIS SECTION APPLY TO THE FULLEST EXTENT PERMITTED BY LAW. Tlon does not disclaim any warranty or other right that Tlon is prohibited from disclaiming under applicable law.
277+ FROM THE SOFTWARE OR TLON ENTITIES OR ANY MATERIALS OR CONTENT
278+ AVAILABLE THROUGH THE SOFTWARE WILL CREATE ANY WARRANTY REGARDING ANY
279+ OF THE TLON ENTITIES OR THE SOFTWARE THAT IS NOT EXPRESSLY STATED IN
280+ THESE TERMS. WE ARE NOT RESPONSIBLE FOR ANY DAMAGE THAT MAY RESULT
281+ FROM THE SOFTWARE.
282+ </p>
283+ <p>
284+ <b>
285+ YOU ARE SOLELY RESPONSIBLE FOR RECORDING AND SAFEGUARDING YOUR KEYS
286+ AND ANY INFORMATION THAT YOU MAY NEED TO RECOVER THE SAME. YOU
287+ UNDERSTAND AND AGREE THAT YOU USE ANY PORTION OF THE SOFTWARE AT
288+ YOUR OWN DISCRETION AND RISK, AND THAT WE ARE NOT RESPONSIBLE FOR
289+ ANY DAMAGE TO YOUR PROPERTY (INCLUDING YOUR COMPUTER SYSTEM OR
290+ MOBILE DEVICE USED IN CONNECTION WITH THE SOFTWARE) OR ANY LOSS OF
291+ DATA OR OTHER PROPERTY OR ASSETS YOU MAY OWN OR CONTROL IN
292+ CONNECTION WITH YOUR USE OF THE SOFTWARE, KEYS OR WALLET, INCLUDING
293+ ANY LOSS OF ACCESS TO YOUR WALLET OR KEYS.
294+ </b>
295+ </p>
296+ <p>
297+ THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS IN THIS SECTION APPLY TO
298+ THE FULLEST EXTENT PERMITTED BY LAW. Tlon does not disclaim any
299+ warranty or other right that Tlon is prohibited from disclaiming under
300+ applicable law.
104 </p>
301 </p>
105 </li>
302 </li>
106
303
107 <li>
304 <li>
108− <p><b>Limitation of Liability</b></p>
305+ <p>
109− <p>TO THE FULLEST EXTENT PERMITTED BY LAW, IN NO EVENT WILL THE TLON ENTITIES BE LIABLE TO YOU FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING DAMAGES FOR LOSS OF PROFITS, GOODWILL, OR ANY OTHER INTANGIBLE LOSS) ARISING OUT OF OR RELATING TO YOUR ACCESS TO OR USE OF, OR YOUR INABILITY TO ACCESS OR USE OR ANY OTHER PROBLEMS WITH, THE SOFTWARE, YOUR WALLET OR KEYS, OR ANY MATERIALS OR CONTENT WITHIN THE SOFTWARE, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), STATUTE, OR ANY OTHER LEGAL THEORY, AND WHETHER OR NOT ANY TLON ENTITY HAS BEEN INFORMED OF THE POSSIBILITY OF DAMAGE.
306+ <b>Limitation of Liability</b>
307+ </p>
308+ <p>
309+ TO THE FULLEST EXTENT PERMITTED BY LAW, IN NO EVENT WILL THE TLON
310+ ENTITIES BE LIABLE TO YOU FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
311+ CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING DAMAGES FOR LOSS OF
312+ PROFITS, GOODWILL, OR ANY OTHER INTANGIBLE LOSS) ARISING OUT OF OR
313+ RELATING TO YOUR ACCESS TO OR USE OF, OR YOUR INABILITY TO ACCESS OR
314+ USE OR ANY OTHER PROBLEMS WITH, THE SOFTWARE, YOUR WALLET OR KEYS, OR
315+ ANY MATERIALS OR CONTENT WITHIN THE SOFTWARE, WHETHER BASED ON
316+ WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), STATUTE, OR ANY OTHER
317+ LEGAL THEORY, AND WHETHER OR NOT ANY TLON ENTITY HAS BEEN INFORMED OF
318+ THE POSSIBILITY OF DAMAGE.
110 </p>
319 </p>
111− <p>TO THE FULLEST EXTENT PERMITTED BY LAW AND EXCEPT AS OTHERWISE SET FORTH IN SECTION 11, THE AGGREGATE LIABILITY OF THE TLON ENTITIES TO YOU FOR ALL CLAIMS ARISING OUT OF OR RELATING TO THE USE OF OR ANY INABILITY TO USE ANY PORTION OF THE SOFTWARE OR OTHERWISE UNDER THESE TERMS, WHETHER IN CONTRACT, TORT, OR OTHERWISE, IS LIMITED TO $100.
320+ <p>
321+ TO THE FULLEST EXTENT PERMITTED BY LAW AND EXCEPT AS OTHERWISE SET
322+ FORTH IN SECTION 11, THE AGGREGATE LIABILITY OF THE TLON ENTITIES TO
323+ YOU FOR ALL CLAIMS ARISING OUT OF OR RELATING TO THE USE OF OR ANY
324+ INABILITY TO USE ANY PORTION OF THE SOFTWARE OR OTHERWISE UNDER THESE
325+ TERMS, WHETHER IN CONTRACT, TORT, OR OTHERWISE, IS LIMITED TO $100.
112 </p>
326 </p>
113− <p>EACH PROVISION OF THESE TERMS THAT PROVIDES FOR A LIMITATION OF LIABILITY, DISCLAIMER OF WARRANTIES, OR EXCLUSION OF DAMAGES IS INTENDED TO AND DOES ALLOCATE THE RISKS BETWEEN THE PARTIES UNDER THESE TERMS. THIS ALLOCATION IS AN ESSENTIAL ELEMENT OF THE BASIS OF THE BARGAIN BETWEEN THE PARTIES. EACH OF THESE PROVISIONS IS SEVERABLE AND INDEPENDENT OF ALL OTHER PROVISIONS OF THESE TERMS. THE LIMITATIONS IN THIS SECTION 10 WILL APPLY EVEN IF ANY LIMITED REMEDY FAILS OF ITS ESSENTIAL PURPOSE.
327+ <p>
328+ EACH PROVISION OF THESE TERMS THAT PROVIDES FOR A LIMITATION OF
329+ LIABILITY, DISCLAIMER OF WARRANTIES, OR EXCLUSION OF DAMAGES IS
330+ INTENDED TO AND DOES ALLOCATE THE RISKS BETWEEN THE PARTIES UNDER
331+ THESE TERMS. THIS ALLOCATION IS AN ESSENTIAL ELEMENT OF THE BASIS OF
332+ THE BARGAIN BETWEEN THE PARTIES. EACH OF THESE PROVISIONS IS SEVERABLE
333+ AND INDEPENDENT OF ALL OTHER PROVISIONS OF THESE TERMS. THE
334+ LIMITATIONS IN THIS SECTION 10 WILL APPLY EVEN IF ANY LIMITED REMEDY
335+ FAILS OF ITS ESSENTIAL PURPOSE.
114 </p>
336 </p>
115 </li>
337 </li>
116
338
117 <li>
339 <li>
118− <p><b>Dispute Resolution and Arbitration</b></p>
340+ <p>
341+ <b>Dispute Resolution and Arbitration</b>
342+ </p>
119 <ol>
343 <ol>
120− <li><p><b>Generally.</b> In the interest of resolving disputes between you and Tlon in the most expedient and cost effective manner, and except as described in Section 11.2, you and Tlon agree that every dispute arising in connection with these Terms will be resolved by binding arbitration. Arbitration is less formal than a lawsuit in court. Arbitration uses a neutral arbitrator instead of a judge or jury, may allow for more limited discovery than in court, and can be subject to very limited review by courts. Arbitrators can award the same damages and relief that a court can award. This agreement to arbitrate disputes includes all claims arising out of or relating to any aspect of these Terms, whether based in contract, tort, statute, fraud, misrepresentation, or any other legal theory, and regardless of whether a claim arises during or after the termination of these Terms. YOU UNDERSTAND AND AGREE THAT, BY ENTERING INTO THESE TERMS, YOU AND Tlon ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
344+ <li>
121− </p></li>
345+ <p>
122− <li><p><b>Exceptions.</b> Despite the provisions of Section 11.1, nothing in these Terms will be deemed to waive, preclude, or otherwise limit the right of either party to: (a) bring an individual action in small claims court; (b) pursue an enforcement action through the applicable federal, state, or local agency if that action is available; (c) seek injunctive relief in a court of law in aid of arbitration; or (d) to file suit in a court of law to address an intellectual property infringement claim.
346+ <b>Generally.</b> In the interest of resolving disputes between
123− </p></li>
347+ you and Tlon in the most expedient and cost effective manner, and
124− <li><p><b>Arbitrator.</b> Any arbitration between you and Tlon will be settled under the Federal Arbitration Act and administered by the American Arbitration Association (“AAA”) under its Consumer Arbitration Rules (collectively, “AAA Rules”) as modified by these Terms. The AAA Rules and filing forms are available online at www.adr.org, by calling the AAA at 1-800-778-7879, or by contacting Tlon. The arbitrator has exclusive authority to resolve any dispute relating to the interpretation, applicability, or enforceability of this binding arbitration agreement.
348+ except as described in Section 11.2, you and Tlon agree that every
125− </p></li>
349+ dispute arising in connection with these Terms will be resolved by
126− <li><p><b>Notice of Arbitration; Process.</b> A party who intends to seek arbitration must first send a written notice of the dispute to the other party by certified U.S. Mail or by Federal Express (signature required) or, only if that other party has not provided a current physical address, then by electronic mail (“<b>Notice of Arbitration</b>”). Tlon’s address for Notice is: Tlon Corporation, 584 Market St. #56196, San Francisco, CA 94104. The Notice of Arbitration must: (a) describe the nature and basis of the claim or dispute; and (b) set forth the specific relief sought (“<b>Demand</b>”). The parties will make good faith efforts to resolve the claim directly, but if the parties do not reach an agreement to do so within 30 days after the Notice of Arbitration is received, you or Tlon may commence an arbitration proceeding. All arbitration proceedings between the parties will be confidential unless otherwise agreed by the parties in writing. During the arbitration, the amount of any settlement offer made by you or Tlon must not be disclosed to the arbitrator until after the arbitrator makes a final decision and award, if any. If the arbitrator awards you an amount higher than the last written settlement amount offered by Tlon in settlement of the dispute prior to the award, Tlon will pay to you the higher of: (i) the amount awarded by the arbitrator; or (ii) $10,000.
350+ binding arbitration. Arbitration is less formal than a lawsuit in
127− </p></li>
351+ court. Arbitration uses a neutral arbitrator instead of a judge or
128− <li><p><b>Fees.</b> If you commence arbitration in accordance with these Terms, Tlon will reimburse you for your payment of the filing fee, unless your claim is for more than $10,000, in which case the payment of any fees will be decided by the AAA Rules. Any arbitration hearing will take place at a location to be agreed upon in San Francisco County, California, but if the claim is for $10,000 or less, you may choose whether the arbitration will be conducted: (a) solely on the basis of documents submitted to the arbitrator; (b) through a non-appearance based telephone hearing; or (c) by an in-person hearing as established by the AAA Rules in the county (or parish) of your billing address. If the arbitrator finds that either the substance of your claim or the relief sought in the Demand is frivolous or brought for an improper purpose (as measured by the standards set forth in Federal Rule of Civil Procedure 11(b)), then the payment of all fees will be governed by the AAA Rules. In that case, you agree to reimburse Tlon for all monies previously disbursed by it that are otherwise your obligation to pay under the AAA Rules. Regardless of the manner in which the arbitration is conducted, the arbitrator must issue a reasoned written decision sufficient to explain the essential findings and conclusions on which the decision and award, if any, are based. The arbitrator may make rulings and resolve disputes as to the payment and reimbursement of fees or expenses at any time during the proceeding and upon request from either party made within 14 days of the arbitrator’s ruling on the merits.
352+ jury, may allow for more limited discovery than in court, and can
129− </p></li>
353+ be subject to very limited review by courts. Arbitrators can award
130− <li><p><b>No Class Actions.</b> YOU AND TLON AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. Further, unless both you and Tlon agree otherwise, the arbitrator may not consolidate more than one person’s claims, and may not otherwise preside over any form of a representative or class proceeding.
354+ the same damages and relief that a court can award. This agreement
131− </p></li>
355+ to arbitrate disputes includes all claims arising out of or
132− <li><p><b>Modifications to this Arbitration Provision.</b> If Tlon makes any future change to this arbitration provision, other than a change to Tlon’s address for Notice of Arbitration, you may reject the change by sending us written notice within 30 days of the change to Tlon’s address for Notice of Arbitration, in which case your account with Tlon will be immediately terminated and this arbitration provision, as in effect immediately prior to the changes you rejected will survive.
356+ relating to any aspect of these Terms, whether based in contract,
133− </p></li>
357+ tort, statute, fraud, misrepresentation, or any other legal
134− <li><p><b>Enforceability.</b> If Section 11.6 is found to be unenforceable or if the entirety of this Section 11 is found to be unenforceable, then the entirety of this Section 11 will be null and void and, in that case, the parties agree that the exclusive jurisdiction and venue described in Section 12 will govern any action arising out of or related to these Terms.
358+ theory, and regardless of whether a claim arises during or after
135− </p></li>
359+ the termination of these Terms. YOU UNDERSTAND AND AGREE THAT, BY
360+ ENTERING INTO THESE TERMS, YOU AND Tlon ARE EACH WAIVING THE RIGHT
361+ TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
362+ </p>
363+ </li>
364+ <li>
365+ <p>
366+ <b>Exceptions.</b> Despite the provisions of Section 11.1, nothing
367+ in these Terms will be deemed to waive, preclude, or otherwise
368+ limit the right of either party to: (a) bring an individual action
369+ in small claims court; (b) pursue an enforcement action through
370+ the applicable federal, state, or local agency if that action is
371+ available; (c) seek injunctive relief in a court of law in aid of
372+ arbitration; or (d) to file suit in a court of law to address an
373+ intellectual property infringement claim.
374+ </p>
375+ </li>
376+ <li>
377+ <p>
378+ <b>Arbitrator.</b> Any arbitration between you and Tlon will be
379+ settled under the Federal Arbitration Act and administered by the
380+ American Arbitration Association (“AAA”) under its Consumer
381+ Arbitration Rules (collectively, “AAA Rules”) as modified by these
382+ Terms. The AAA Rules and filing forms are available online at
383+ www.adr.org, by calling the AAA at 1-800-778-7879, or by
384+ contacting Tlon. The arbitrator has exclusive authority to resolve
385+ any dispute relating to the interpretation, applicability, or
386+ enforceability of this binding arbitration agreement.
387+ </p>
388+ </li>
389+ <li>
390+ <p>
391+ <b>Notice of Arbitration; Process.</b> A party who intends to seek
392+ arbitration must first send a written notice of the dispute to the
393+ other party by certified U.S. Mail or by Federal Express
394+ (signature required) or, only if that other party has not provided
395+ a current physical address, then by electronic mail (“
396+ <b>Notice of Arbitration</b>”). Tlon’s address for Notice is: Tlon
397+ Corporation, 584 Market St. #56196, San Francisco, CA 94104. The
398+ Notice of Arbitration must: (a) describe the nature and basis of
399+ the claim or dispute; and (b) set forth the specific relief sought
400+ (“<b>Demand</b>”). The parties will make good faith efforts to
401+ resolve the claim directly, but if the parties do not reach an
402+ agreement to do so within 30 days after the Notice of Arbitration
403+ is received, you or Tlon may commence an arbitration proceeding.
404+ All arbitration proceedings between the parties will be
405+ confidential unless otherwise agreed by the parties in writing.
406+ During the arbitration, the amount of any settlement offer made by
407+ you or Tlon must not be disclosed to the arbitrator until after
408+ the arbitrator makes a final decision and award, if any. If the
409+ arbitrator awards you an amount higher than the last written
410+ settlement amount offered by Tlon in settlement of the dispute
411+ prior to the award, Tlon will pay to you the higher of: (i) the
412+ amount awarded by the arbitrator; or (ii) $10,000.
413+ </p>
414+ </li>
415+ <li>
416+ <p>
417+ <b>Fees.</b> If you commence arbitration in accordance with these
418+ Terms, Tlon will reimburse you for your payment of the filing fee,
419+ unless your claim is for more than $10,000, in which case the
420+ payment of any fees will be decided by the AAA Rules. Any
421+ arbitration hearing will take place at a location to be agreed
422+ upon in San Francisco County, California, but if the claim is for
423+ $10,000 or less, you may choose whether the arbitration will be
424+ conducted: (a) solely on the basis of documents submitted to the
425+ arbitrator; (b) through a non-appearance based telephone hearing;
426+ or (c) by an in-person hearing as established by the AAA Rules in
427+ the county (or parish) of your billing address. If the arbitrator
428+ finds that either the substance of your claim or the relief sought
429+ in the Demand is frivolous or brought for an improper purpose (as
430+ measured by the standards set forth in Federal Rule of Civil
431+ Procedure 11(b)), then the payment of all fees will be governed by
432+ the AAA Rules. In that case, you agree to reimburse Tlon for all
433+ monies previously disbursed by it that are otherwise your
434+ obligation to pay under the AAA Rules. Regardless of the manner in
435+ which the arbitration is conducted, the arbitrator must issue a
436+ reasoned written decision sufficient to explain the essential
437+ findings and conclusions on which the decision and award, if any,
438+ are based. The arbitrator may make rulings and resolve disputes as
439+ to the payment and reimbursement of fees or expenses at any time
440+ during the proceeding and upon request from either party made
441+ within 14 days of the arbitrator’s ruling on the merits.
442+ </p>
443+ </li>
444+ <li>
445+ <p>
446+ <b>No Class Actions.</b> YOU AND TLON AGREE THAT EACH MAY BRING
447+ CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY
448+ AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR
449+ REPRESENTATIVE PROCEEDING. Further, unless both you and Tlon agree
450+ otherwise, the arbitrator may not consolidate more than one
451+ person’s claims, and may not otherwise preside over any form of a
452+ representative or class proceeding.
453+ </p>
454+ </li>
455+ <li>
456+ <p>
457+ <b>Modifications to this Arbitration Provision.</b> If Tlon makes
458+ any future change to this arbitration provision, other than a
459+ change to Tlon’s address for Notice of Arbitration, you may reject
460+ the change by sending us written notice within 30 days of the
461+ change to Tlon’s address for Notice of Arbitration, in which case
462+ your account with Tlon will be immediately terminated and this
463+ arbitration provision, as in effect immediately prior to the
464+ changes you rejected will survive.
465+ </p>
466+ </li>
467+ <li>
468+ <p>
469+ <b>Enforceability.</b> If Section 11.6 is found to be
470+ unenforceable or if the entirety of this Section 11 is found to be
471+ unenforceable, then the entirety of this Section 11 will be null
472+ and void and, in that case, the parties agree that the exclusive
473+ jurisdiction and venue described in Section 12 will govern any
474+ action arising out of or related to these Terms.
475+ </p>
476+ </li>
136 </ol>
477 </ol>
137 </li>
478 </li>
138
479
139− <li><p><b>Miscellaneous.</b> Your use of the Software is subject to all additional terms, policies, rules, or guidelines applicable to the Software or certain features of the Software that we may post on or link to from the Software (the “Additional Terms”). All Additional Terms are incorporated by this reference into, and made a part of, these Terms. You may not assign or transfer these Terms or your rights under these Terms, in whole or in part, by operation of law or otherwise, without our prior written consent. We may assign these Terms at any time without notice or consent. The failure to require performance of any provision will not affect our right to require performance at any other time after that, nor will a waiver by us of any breach or default of these Terms, or any provision of these Terms, be a waiver of any subsequent breach or default or a waiver of the provision itself. If any part of these Terms is held to be invalid or unenforceable, the unenforceable part will be given effect to the greatest extent possible, and the remaining parts will remain in full force and effect. These Terms are governed by the laws of the State of California without regard to conflict of law principles. You and Tlon submit to the personal and exclusive jurisdiction of the state courts and federal courts located within San Francisco County, California for resolution of any lawsuit or court proceeding permitted under these Terms. We are under no obligation to provide support for the Software. In instances where we may offer support, the support will be subject to published policies.
480+ <li>
140− </p></li>
481+ <p>
141−
482+ <b>Miscellaneous.</b> Your use of the Software is subject to all
483+ additional terms, policies, rules, or guidelines applicable to the
484+ Software or certain features of the Software that we may post on or
485+ link to from the Software (the “Additional Terms”). All Additional
486+ Terms are incorporated by this reference into, and made a part of,
487+ these Terms. You may not assign or transfer these Terms or your rights
488+ under these Terms, in whole or in part, by operation of law or
489+ otherwise, without our prior written consent. We may assign these
490+ Terms at any time without notice or consent. The failure to require
491+ performance of any provision will not affect our right to require
492+ performance at any other time after that, nor will a waiver by us of
493+ any breach or default of these Terms, or any provision of these Terms,
494+ be a waiver of any subsequent breach or default or a waiver of the
495+ provision itself. If any part of these Terms is held to be invalid or
496+ unenforceable, the unenforceable part will be given effect to the
497+ greatest extent possible, and the remaining parts will remain in full
498+ force and effect. These Terms are governed by the laws of the State of
499+ California without regard to conflict of law principles. You and Tlon
500+ submit to the personal and exclusive jurisdiction of the state courts
501+ and federal courts located within San Francisco County, California for
502+ resolution of any lawsuit or court proceeding permitted under these
503+ Terms. We are under no obligation to provide support for the Software.
504+ In instances where we may offer support, the support will be subject
505+ to published policies.
506+ </p>
507+ </li>
142 </ol>
508 </ol>
143−
144 </span>
509 </span>
510+);
145
511
146−export default EulaText
512+export default EulaText;
src/walletgen/components/Footer.js+24−28
@@ -1,46 +1,42 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import { version } from '../../../package.json'
3+import { version } from '../../../package.json';
4
4
5class Footer extends React.Component {
5class Footer extends React.Component {
6−
7 render() {
6 render() {
8 // eslint-disable-next-line
7 // eslint-disable-next-line
9− const { setGlobalState, route } = this.props
8+ const { setGlobalState, route } = this.props;
10
9
11 return (
10 return (
12 <footer className={'row h-13 mt-20'}>
11 <footer className={'row h-13 mt-20'}>
12+ <div className={'col-md-3'}>{'Urbit Wallet Generator v' + version}</div>
13+
14+ <div className={'col-md-3'}>{'Tlon Corporation'}</div>
13
15
14 <div className={'col-md-3'}>
16 <div className={'col-md-3'}>
15− {'Urbit Wallet Generator v' + version }
17+ {/* eslint-disable-next-line */}
18+ <a
19+ href={'#'}
20+ onClick={() =>
21+ setGlobalState({ route: '/EulaView', lastRoute: route })
22+ }>
23+ EULA
24+ </a>
16 </div>
25 </div>
17
26
18 <div className={'col-md-3'}>
27 <div className={'col-md-3'}>
19− {'Tlon Corporation'}
28+ {/* eslint-disable-next-line */}
29+ <a
30+ href={'#'}
31+ onClick={() =>
32+ setGlobalState({ route: '/PrivacyPolicy', lastRoute: route })
33+ }>
34+ Privacy Policy
35+ </a>
20 </div>
36 </div>
21−
22− <div className={'col-md-3'}>
23− {/* eslint-disable-next-line */}
24− <a
25− href={'#'}
26− onClick={() => setGlobalState({ 'route': '/EulaView', 'lastRoute': route })}>
27− EULA
28− </a>
29− </div>
30−
31− <div className={'col-md-3'}>
32− {/* eslint-disable-next-line */}
33− <a
34− href={'#'}
35− onClick={() => setGlobalState({ 'route': '/PrivacyPolicy', 'lastRoute': route })}
36− >
37− Privacy Policy
38− </a>
39− </div>
40−
41 </footer>
37 </footer>
42− )
38+ );
43 }
39 }
44}
40}
45
41
46−export default Footer
42+export default Footer;
src/walletgen/components/Header.js+62−38
@@ -1,86 +1,110 @@
1−import React from 'react'
1+import React from 'react';
2−import { seq } from '../lib/lib'
2+import { seq } from '../lib/lib';
3
3
4−import { NETWORK_STATES } from '../lib/constants'
4+import { NETWORK_STATES } from '../lib/constants';
5
5
6−const StatusIndicator = props =>
6+const StatusIndicator = props => (
7 <div className={`flex h-9 align-center ${props.c2} flex-shrink-0`}>
7 <div className={`flex h-9 align-center ${props.c2} flex-shrink-0`}>
8 <div className={`${props.c1} h-9`}>
8 <div className={`${props.c1} h-9`}>
9− {
9+ {props.error === true ? <Ex /> : <Check />}
10− props.error === true ? <Ex /> : <Check />
11− }
12 </div>
10 </div>
13− <p className={'pr-4 text-sm text-600 c-white m-0'}>
11+ <p className={'pr-4 text-sm text-600 c-white m-0'}>{props.message}</p>
14− { props.message }
15− </p>
16 </div>
12 </div>
13+);
17
14
18const ChooseIndicator = props =>
15const ChooseIndicator = props =>
19− props.network === NETWORK_STATES.ONLINE
16+ props.network === NETWORK_STATES.ONLINE ? (
20− ? <StatusIndicator
17+ <StatusIndicator
21 message={'You are online.'}
18 message={'You are online.'}
22 c1={'bg-red'}
19 c1={'bg-red'}
23 c2={'bg-red'}
20 c2={'bg-red'}
24 error
21 error
25 />
22 />
26− : props.network === NETWORK_STATES.OFFLINE
23+ ) : props.network === NETWORK_STATES.OFFLINE ? (
27− ? <StatusIndicator
24+ <StatusIndicator
28 message={'You are offline.'}
25 message={'You are offline.'}
29 c1={'bg-green'}
26 c1={'bg-green'}
30 c2={'bg-green'}
27 c2={'bg-green'}
31 />
28 />
32− : <StatusIndicator
29+ ) : (
30+ <StatusIndicator
33 message={'Checking network connection..'}
31 message={'Checking network connection..'}
34 c1={'bg-orange'}
32 c1={'bg-orange'}
35 c2={'bg-orange'}
33 c2={'bg-orange'}
36 />
34 />
35+ );
37
36
38−const ProgressIndicator = ({ currentStep, totalSteps }) =>
37+const ProgressIndicator = ({ currentStep, totalSteps }) => (
39 <div className={'relative h-9 col- mb-0'}>
38 <div className={'relative h-9 col- mb-0'}>
40− <div className={'absolute ml-3 h-9 flex align-center text-sm white text-600'}>{`Step ${currentStep} of ${totalSteps}`}</div>
39+ <div
40+ className={
41+ 'absolute ml-3 h-9 flex align-center text-sm white text-600'
42+ }>{`Step ${currentStep} of ${totalSteps}`}</div>
41 <div className={'flex'}>
43 <div className={'flex'}>
42− {
44+ {seq(totalSteps).map((step, index) => (
43− seq(totalSteps).map((step, index) => <div key={index} className={`${ index+1 > currentStep ? 'bg-gray' : 'bg-blue' } h-9 col-`}>{''}</div> )
45+ <div
44− }
46+ key={index}
47+ className={`${
48+ index + 1 > currentStep ? 'bg-gray' : 'bg-blue'
49+ } h-9 col-`}>
50+ {''}
51+ </div>
52+ ))}
45 </div>
53 </div>
46−
47 </div>
54 </div>
55+);
48
56
49−
57+const Header = props => (
50−const Header = props =>
51 <header className={'col'}>
58 <header className={'col'}>
52 <div className={'flex justify-between'}>
59 <div className={'flex justify-between'}>
53 <ProgressIndicator
60 <ProgressIndicator
54 totalSteps={props.totalSteps}
61 totalSteps={props.totalSteps}
55 currentStep={props.currentStep}
62 currentStep={props.currentStep}
56 />
63 />
57− <ChooseIndicator
64+ <ChooseIndicator network={props.network} />
58− network={ props.network }
59− />
60 </div>
65 </div>
61 <div className={'col-md-8 h-15 flex align-center'}>
66 <div className={'col-md-8 h-15 flex align-center'}>
62 <h1 className={'m-0 p-0'}>{'Wallet Generator'}</h1>
67 <h1 className={'m-0 p-0'}>{'Wallet Generator'}</h1>
63 </div>
68 </div>
64 </header>
69 </header>
65−
70+);
66−
67
71
68const Ex = () => {
72const Ex = () => {
69 return (
73 return (
70− <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
74+ <svg
75+ width="36"
76+ height="36"
77+ viewBox="0 0 36 36"
78+ fill="none"
79+ xmlns="http://www.w3.org/2000/svg">
71 <rect width="36" height="36" />
80 <rect width="36" height="36" />
72− <path fillRule="evenodd" clipRule="evenodd" d="M16.2857 18L12 22.2857L13.7143 24L18 19.7143L22.2857 24L24 22.2857L19.7143 18L23.9999 13.7143L22.2857 12L18 16.2857L13.7143 12L12 13.7143L16.2857 18Z" fill="white"/>
81+ <path
82+ fillRule="evenodd"
83+ clipRule="evenodd"
84+ d="M16.2857 18L12 22.2857L13.7143 24L18 19.7143L22.2857 24L24 22.2857L19.7143 18L23.9999 13.7143L22.2857 12L18 16.2857L13.7143 12L12 13.7143L16.2857 18Z"
85+ fill="white"
86+ />
73 </svg>
87 </svg>
74− )
88+ );
75−}
89+};
76
90
77const Check = () => {
91const Check = () => {
78 return (
92 return (
79− <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
93+ <svg
80− <rect width="36" height="36"/>
94+ width="36"
81− <path fillRule="evenodd" clipRule="evenodd" d="M24.9999 14.6329L23.2766 13L16.1699 19.7341L12.7233 16.4682L11 18.1012L16.1699 23L17.2451 21.9812L17.8932 21.3671L24.9999 14.6329Z" fill="white"/>
95+ height="36"
96+ viewBox="0 0 36 36"
97+ fill="none"
98+ xmlns="http://www.w3.org/2000/svg">
99+ <rect width="36" height="36" />
100+ <path
101+ fillRule="evenodd"
102+ clipRule="evenodd"
103+ d="M24.9999 14.6329L23.2766 13L16.1699 19.7341L12.7233 16.4682L11 18.1012L16.1699 23L17.2451 21.9812L17.8932 21.3671L24.9999 14.6329Z"
104+ fill="white"
105+ />
82 </svg>
106 </svg>
83− )
107+ );
84−}
108+};
85
109
86−export default Header
110+export default Header;
src/walletgen/components/InputBox.js+9−9
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
2
2
3const InputBox = ({
3const InputBox = ({
4 disabled,
4 disabled,
@@ -9,7 +9,7 @@ const InputBox = ({
9 onChange,
9 onChange,
10 currentValue,
10 currentValue,
11 placeholder,
11 placeholder,
12− containerClassnames
12+ containerClassnames,
13}) => {
13}) => {
14 return (
14 return (
15 <div className={containerClassnames}>
15 <div className={containerClassnames}>
@@ -17,15 +17,15 @@ const InputBox = ({
17 <input
17 <input
18 className={className}
18 className={className}
19 placeholder={placeholder}
19 placeholder={placeholder}
20− type='text'
20+ type="text"
21 value={currentValue}
21 value={currentValue}
22 autoFocus={autofocus}
22 autoFocus={autofocus}
23 autoComplete={autocomplete}
23 autoComplete={autocomplete}
24 disabled={disabled}
24 disabled={disabled}
25− onChange={e => onChange(e)} />
25+ onChange={e => onChange(e)}
26− </div> )
26+ />
27−}
27+ </div>
28+ );
29+};
28
30
29−
31+export default InputBox;
30−
31−export default InputBox
src/walletgen/components/ReactSVGComponents.js+37−21
@@ -1,63 +1,79 @@
1−import React from 'react'
1+import React from 'react';
2−import { get, map } from 'lodash'
2+import { get, map } from 'lodash';
3
3
4const ReactSVGComponents = {
4const ReactSVGComponents = {
5 svg: p => {
5 svg: p => {
6 return (
6 return (
7 <svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'}>
7 <svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'}>
8− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
8+ {map(get(p, 'children', []), child =>
9+ ReactSVGComponents[child.tag](child)
10+ )}
9 </svg>
11 </svg>
10− )
12+ );
11 },
13 },
12 circle: p => {
14 circle: p => {
13 return (
15 return (
14 <circle {...p.attr}>
16 <circle {...p.attr}>
15− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
17+ {map(get(p, 'children', []), child =>
18+ ReactSVGComponents[child.tag](child)
19+ )}
16 </circle>
20 </circle>
17− )
21+ );
18 },
22 },
19 rect: p => {
23 rect: p => {
20 return (
24 return (
21 <rect {...p.attr}>
25 <rect {...p.attr}>
22− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
26+ {map(get(p, 'children', []), child =>
27+ ReactSVGComponents[child.tag](child)
28+ )}
23 </rect>
29 </rect>
24− )
30+ );
25 },
31 },
26 path: p => {
32 path: p => {
27 return (
33 return (
28 <path {...p.attr}>
34 <path {...p.attr}>
29− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
35+ {map(get(p, 'children', []), child =>
36+ ReactSVGComponents[child.tag](child)
37+ )}
30 </path>
38 </path>
31− )
39+ );
32 },
40 },
33 g: p => {
41 g: p => {
34 return (
42 return (
35 <g {...p.attr}>
43 <g {...p.attr}>
36− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
44+ {map(get(p, 'children', []), child =>
45+ ReactSVGComponents[child.tag](child)
46+ )}
37 </g>
47 </g>
38− )
48+ );
39 },
49 },
40 polygon: p => {
50 polygon: p => {
41 return (
51 return (
42 <polygon {...p.attr}>
52 <polygon {...p.attr}>
43− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
53+ {map(get(p, 'children', []), child =>
54+ ReactSVGComponents[child.tag](child)
55+ )}
44 </polygon>
56 </polygon>
45− )
57+ );
46 },
58 },
47 line: p => {
59 line: p => {
48 return (
60 return (
49 <line {...p.attr}>
61 <line {...p.attr}>
50− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
62+ {map(get(p, 'children', []), child =>
63+ ReactSVGComponents[child.tag](child)
64+ )}
51 </line>
65 </line>
52− )
66+ );
53 },
67 },
54 polyline: p => {
68 polyline: p => {
55 return (
69 return (
56 <polyline {...p.attr}>
70 <polyline {...p.attr}>
57− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
71+ {map(get(p, 'children', []), child =>
72+ ReactSVGComponents[child.tag](child)
73+ )}
58 </polyline>
74 </polyline>
59− )
75+ );
60− }
76+ },
61−}
77+};
62
78
63−export default ReactSVGComponents
79+export default ReactSVGComponents;
src/walletgen/components/UploadButton.js+14−12
@@ -12,18 +12,20 @@ class UploadButton extends Component {
12
12
13 render() {
13 render() {
14 return (
14 return (
15− <label className={this.props.className}>
15+ <label className={this.props.className}>
16− <input
16+ <input
17− className={'hidden'}
17+ className={'hidden'}
18− accept={'.txt'}
18+ accept={'.txt'}
19− ref={(ref) => { this.input = ref; }}
19+ ref={ref => {
20− onChange={this.onChange}
20+ this.input = ref;
21− disabled={this.props.disabled}
21+ }}
22− type="file"
22+ onChange={this.onChange}
23− />
23+ disabled={this.props.disabled}
24− {this.props.children}
24+ type="file"
25− </label>
25+ />
26− );
26+ {this.props.children}
27+ </label>
28+ );
27 }
29 }
28}
30}
29
31
src/walletgen/lib/constants.js+16−16
@@ -1,21 +1,21 @@
1−const MIN_GALAXY = 0
1+const MIN_GALAXY = 0;
2−const MAX_GALAXY = 255
2+const MAX_GALAXY = 255;
3−const MIN_STAR = 256
3+const MIN_STAR = 256;
4−const MAX_STAR = 65535
4+const MAX_STAR = 65535;
5−const MIN_PLANET = 65536
5+const MIN_PLANET = 65536;
6−const MAX_PLANET = 4294967297
6+const MAX_PLANET = 4294967297;
7
7
8−const PLANET_ENTROPY_BITS = 64
8+const PLANET_ENTROPY_BITS = 64;
9−const STAR_ENTROPY_BITS = 128
9+const STAR_ENTROPY_BITS = 128;
10−const GALAXY_ENTROPY_BITS = 384
10+const GALAXY_ENTROPY_BITS = 384;
11
11
12−const SEED_ENTROPY_BITS = 128
12+const SEED_ENTROPY_BITS = 128;
13
13
14const NETWORK_STATES = {
14const NETWORK_STATES = {
15 ONLINE: Symbol('ONLINE'),
15 ONLINE: Symbol('ONLINE'),
16 OFFLINE: Symbol('OFFLINE'),
16 OFFLINE: Symbol('OFFLINE'),
17 UNKNOWN: Symbol('UNKNOWN'),
17 UNKNOWN: Symbol('UNKNOWN'),
18−}
18+};
19
19
20const GEN_STATES = {
20const GEN_STATES = {
21 ENY_NOSTART: Symbol('ENY_NOSTART'),
21 ENY_NOSTART: Symbol('ENY_NOSTART'),
@@ -30,14 +30,14 @@ const GEN_STATES = {
30 PAPER_PENDING: Symbol('PAPER_PENDING'),
30 PAPER_PENDING: Symbol('PAPER_PENDING'),
31 PAPER_SUCCESS: Symbol('PAPER_SUCCESS'),
31 PAPER_SUCCESS: Symbol('PAPER_SUCCESS'),
32 PAPER_FAILURE: Symbol('PAPER_FAILURE'),
32 PAPER_FAILURE: Symbol('PAPER_FAILURE'),
33−}
33+};
34
34
35const BUTTON_STATES = {
35const BUTTON_STATES = {
36 NOSTART: Symbol('NOSTART'),
36 NOSTART: Symbol('NOSTART'),
37 SUCCESS: Symbol('SUCCESS'),
37 SUCCESS: Symbol('SUCCESS'),
38 ERROR: Symbol('ERROR'),
38 ERROR: Symbol('ERROR'),
39 PENDING: Symbol('PENDING'),
39 PENDING: Symbol('PENDING'),
40−}
40+};
41
41
42const PROFILE_STATES = {
42const PROFILE_STATES = {
43 NOSTART: Symbol('NOSTART'),
43 NOSTART: Symbol('NOSTART'),
@@ -45,7 +45,7 @@ const PROFILE_STATES = {
45 UPLOAD_ERROR: Symbol('UPLOAD_ERROR'),
45 UPLOAD_ERROR: Symbol('UPLOAD_ERROR'),
46 INPUT_SUCCESS: Symbol('INPUT_SUCCESS'),
46 INPUT_SUCCESS: Symbol('INPUT_SUCCESS'),
47 INPUT_ERROR: Symbol('INPUT_ERROR'),
47 INPUT_ERROR: Symbol('INPUT_ERROR'),
48−}
48+};
49
49
50export {
50export {
51 NETWORK_STATES,
51 NETWORK_STATES,
@@ -61,5 +61,5 @@ export {
61 PLANET_ENTROPY_BITS,
61 PLANET_ENTROPY_BITS,
62 STAR_ENTROPY_BITS,
62 STAR_ENTROPY_BITS,
63 GALAXY_ENTROPY_BITS,
63 GALAXY_ENTROPY_BITS,
64− SEED_ENTROPY_BITS
64+ SEED_ENTROPY_BITS,
65−}
65+};
src/walletgen/lib/lib.js+38−41
@@ -1,54 +1,56 @@
1−
1+import * as ob from 'urbit-ob';
2−import * as ob from 'urbit-ob'
2+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
3−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
3+import * as more from 'more-entropy';
4−import * as more from 'more-entropy'
4+import lodash from 'lodash';
5−import lodash from 'lodash'
6import {
5import {
7− MIN_STAR, MIN_PLANET, SEED_ENTROPY_BITS,
6+ MIN_STAR,
8− GALAXY_ENTROPY_BITS, STAR_ENTROPY_BITS, PLANET_ENTROPY_BITS,
7+ MIN_PLANET,
9−} from './constants'
8+ SEED_ENTROPY_BITS,
9+ GALAXY_ENTROPY_BITS,
10+ STAR_ENTROPY_BITS,
11+ PLANET_ENTROPY_BITS,
12+} from './constants';
10
13
11−const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8
14+const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8;
12
15
13// returns a promise for a ticket string
16// returns a promise for a ticket string
14const makeTicket = point => {
17const makeTicket = point => {
15−
18+ const bits =
16− const bits = point < MIN_STAR
19+ point < MIN_STAR
17− ? GALAXY_ENTROPY_BITS
20+ ? GALAXY_ENTROPY_BITS
18− : point < MIN_PLANET
21+ : point < MIN_PLANET
19 ? STAR_ENTROPY_BITS
22 ? STAR_ENTROPY_BITS
20− : PLANET_ENTROPY_BITS
23+ : PLANET_ENTROPY_BITS;
21
24
22− const bytes = bits / 8
25+ const bytes = bits / 8;
23− const some = new Uint8Array(bytes)
26+ const some = new Uint8Array(bytes);
24− window.crypto.getRandomValues(some)
27+ window.crypto.getRandomValues(some);
25
28
26− const gen = new more.Generator()
29+ const gen = new more.Generator();
27
30
28 return new Promise((resolve, reject) => {
31 return new Promise((resolve, reject) => {
29 gen.generate(bits, result => {
32 gen.generate(bits, result => {
30− const chunked = lodash.chunk(result, 2)
33+ const chunked = lodash.chunk(result, 2);
31− const desired = chunked.slice(0, bytes) // only take required entropy
34+ const desired = chunked.slice(0, bytes); // only take required entropy
32− const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
35+ const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1]);
33− const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
36+ const entropy = lodash.zipWith(some, more, (x, y) => x ^ y);
34− const buf = Buffer.from(entropy)
37+ const buf = Buffer.from(entropy);
35− const patq = ob.hex2patq(buf.toString('hex'))
38+ const patq = ob.hex2patq(buf.toString('hex'));
36− resolve(patq)
39+ resolve(patq);
37− reject('Entropy generation failed')
40+ reject('Entropy generation failed');
38− })
41+ });
39− })
42+ });
40−}
43+};
41
44
42// return a wallet object
45// return a wallet object
43const generateWallet = async (point, ticket, boot) => {
46const generateWallet = async (point, ticket, boot) => {
44−
45 const config = {
47 const config = {
46 ticket: ticket,
48 ticket: ticket,
47 seedSize: SEED_LENGTH_BYTES,
49 seedSize: SEED_LENGTH_BYTES,
48 ship: point,
50 ship: point,
49 password: '',
51 password: '',
50 revisions: {},
52 revisions: {},
51− boot: boot
53+ boot: boot,
52 };
54 };
53
55
54 const wallet = await kg.generateWallet(config);
56 const wallet = await kg.generateWallet(config);
@@ -58,20 +60,15 @@ const generateWallet = async (point, ticket, boot) => {
58 console.log('Generating Wallet for point address: ', point);
60 console.log('Generating Wallet for point address: ', point);
59
61
60 return wallet;
62 return wallet;
61−}
63+};
62
64
63// returns a promise
65// returns a promise
64const generateOwnershipWallet = (ship, ticket) =>
66const generateOwnershipWallet = (ship, ticket) =>
65 kg.generateOwnershipWallet({ ship, ticket });
67 kg.generateOwnershipWallet({ ship, ticket });
66
68
67−const seq = num => Array.from(Array(num), (_, i) => i)
69+const seq = num => Array.from(Array(num), (_, i) => i);
68
70
69−const compose = (...fs) => fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v)
71+const compose = (...fs) =>
72+ fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v);
70
73
71−export {
74+export { makeTicket, generateWallet, generateOwnershipWallet, compose, seq };
72− makeTicket,
73− generateWallet,
74− generateOwnershipWallet,
75− compose,
76− seq,
77−}
src/walletgen/views/BrowserWarning.js+6−7
@@ -1,14 +1,13 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−const BrowserWarning = (props) =>
3+const BrowserWarning = props => (
4 <div>
4 <div>
5 <h2>{'You must use this tool with Google Chrome.'}</h2>
5 <h2>{'You must use this tool with Google Chrome.'}</h2>
6 <p>
6 <p>
7− {
7+ {`To continue, open this HTML file with Google Chrome. We don’t like the
8− `To continue, open this HTML file with Google Chrome. We don’t like the
8+ new bubble UI either, but it's still the most secure browser.`}
9− new bubble UI either, but it's still the most secure browser.`
10− }
11 </p>
9 </p>
12 </div>
10 </div>
11+);
13
12
14−export default BrowserWarning
13+export default BrowserWarning;
src/walletgen/views/Custody.js+9−8
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import Button from '../components/Button'
3+import Button from '../components/Button';
4
4
5const NEXT_STEP_NUM = 5;
5const NEXT_STEP_NUM = 5;
6
6
7class Custody extends React.Component {
7class Custody extends React.Component {
8 render() {
8 render() {
9− let { setGlobalState } = this.props
9+ let { setGlobalState } = this.props;
10
10
11− return(
11+ return (
12 <div className={'col-md-6'}>
12 <div className={'col-md-6'}>
13 <h2 className={'mb-4'}>{`A Word On Custody`}</h2>
13 <h2 className={'mb-4'}>{`A Word On Custody`}</h2>
14
14
@@ -24,13 +24,14 @@ class Custody extends React.Component {
24 <Button
24 <Button
25 className={'btn btn-primary'}
25 className={'btn btn-primary'}
26 text={'I Understand →'}
26 text={'I Understand →'}
27− onClick={ () => setGlobalState({ route: '/Generate', 'currentStep': NEXT_STEP_NUM })}
27+ onClick={() =>
28+ setGlobalState({ route: '/Generate', currentStep: NEXT_STEP_NUM })
29+ }
28 />
30 />
29 </div>
31 </div>
30−
31 </div>
32 </div>
32− )
33+ );
33 }
34 }
34}
35}
35
36
36−export default Custody
37+export default Custody;
src/walletgen/views/Done.js+28−12
@@ -1,15 +1,16 @@
1−import React from 'react'
1+import React from 'react';
2
2
3class Done extends React.Component {
3class Done extends React.Component {
4 render() {
4 render() {
5− return(
5+ return (
6 <div className={'col-md-7'}>
6 <div className={'col-md-7'}>
7−
8 <h2>{`Next Steps`}</h2>
7 <h2>{`Next Steps`}</h2>
9
8
10 <ol>
9 <ol>
11 <li className={'mb-8'}>
10 <li className={'mb-8'}>
12− <span className="h3">You <b>must</b> close this tab before connecting to the internet.</span>
11+ <span className="h3">
12+ You <b>must</b> close this tab before connecting to the internet.
13+ </span>
13 </li>
14 </li>
14
15
15 <li className={'mb-8'}>
16 <li className={'mb-8'}>
@@ -20,7 +21,11 @@ class Done extends React.Component {
20 </li>
21 </li>
21
22
22 <li className={'mb-8'}>
23 <li className={'mb-8'}>
23− <span className="h3">{'Upload your '}<code>{'urbit-registration.txt'}</code>{' to Registration'}</span>
24+ <span className="h3">
25+ {'Upload your '}
26+ <code>{'urbit-registration.txt'}</code>
27+ {' to Registration'}
28+ </span>
24 <p className={'mt-0 ml-6'}>
29 <p className={'mt-0 ml-6'}>
25 {`If you kept the Registration tab open, you can simply return there to upload. Otherwise, you can log into Registration again with the code in your email and navigate back to where you left off. If you do not upload this file to Registration, we will have no way to transfer your
30 {`If you kept the Registration tab open, you can simply return there to upload. Otherwise, you can log into Registration again with the code in your email and navigate back to where you left off. If you do not upload this file to Registration, we will have no way to transfer your
26 ships to you. This means you could lose your ships.`}
31 ships to you. This means you could lose your ships.`}
@@ -29,18 +34,29 @@ class Done extends React.Component {
29
34
30 <li className={'mb-8'}>
35 <li className={'mb-8'}>
31 <span className="h3">Print your paper wallets</span>
36 <span className="h3">Print your paper wallets</span>
32− <p className={'ml-6 mv-0'}>You will lose your assets if you lose your wallets. Do not use a public printer. Please note that most modern printers contain immutable memory of all printed materials.</p>
37+ <p className={'ml-6 mv-0'}>
38+ You will lose your assets if you lose your wallets. Do not use a
39+ public printer. Please note that most modern printers contain
40+ immutable memory of all printed materials.
41+ </p>
33 </li>
42 </li>
34
43
35 <li className={'mb-8'}>
44 <li className={'mb-8'}>
36− <span className="h3">Securely erase the <span className={'text-mono'}>.zip</span> file and your wallet <span className={'text-mono'}>.png</span> files</span>
45+ <span className="h3">
37− <p className={'ml-6 mv-0'}>If you used an airgapped machine, strongly consider destroying this machine.</p>
46+ Securely erase the <span className={'text-mono'}>.zip</span> file
47+ and your wallet <span className={'text-mono'}>.png</span> files
48+ </span>
49+ <p className={'ml-6 mv-0'}>
50+ If you used an airgapped machine, strongly consider destroying
51+ this machine.
52+ </p>
38 </li>
53 </li>
39
54
40 <li className={'mb-8'}>
55 <li className={'mb-8'}>
41− <span className="h3">If you understand the above steps, you may now close this tab.</span>
56+ <span className="h3">
57+ If you understand the above steps, you may now close this tab.
58+ </span>
42 </li>
59 </li>
43−
44 </ol>
60 </ol>
45 {
61 {
46 // <CheckBox
62 // <CheckBox
@@ -56,8 +72,8 @@ class Done extends React.Component {
56 // />
72 // />
57 }
73 }
58 </div>
74 </div>
59− )
75+ );
60 }
76 }
61}
77}
62
78
63−export default Done
79+export default Done;
src/walletgen/views/Download.js+105−84
@@ -1,40 +1,42 @@
1−import React from 'react'
1+import React from 'react';
2−import saveAs from 'file-saver'
2+import saveAs from 'file-saver';
3−import * as lodash from 'lodash'
3+import * as lodash from 'lodash';
4import JSZip from 'jszip';
4import JSZip from 'jszip';
5
5
6−import Button from '../components/Button'
6+import Button from '../components/Button';
7−import CheckBox from '../components/CheckBox'
7+import CheckBox from '../components/CheckBox';
8
8
9const NEXT_STEP_NUM = 7;
9const NEXT_STEP_NUM = 7;
10
10
11class Download extends React.Component {
11class Download extends React.Component {
12− handleSave = (event) => {
12+ handleSave = event => {
13− const { props } = this
13+ const { props } = this;
14−
14+
15− const wallets = props['wallets.value']
15+ const wallets = props['wallets.value'];
16− const profile = props['profile.value']
16+ const profile = props['profile.value'];
17− const paper = props['paperCollateral.value']
17+ const paper = props['paperCollateral.value'];
18−
18+
19− const urbit_registration = Object.entries(wallets).reduce((acc, [k, v]) => {
19+ const urbit_registration = Object.entries(wallets).reduce(
20− const owner = lodash.get(v, 'ownership.keys.address', '')
20+ (acc, [k, v]) => {
21− const transfer = ''
21+ const owner = lodash.get(v, 'ownership.keys.address', '');
22− const spawn = lodash.get(v, 'spawn.keys.address', '')
22+ const transfer = '';
23− const manage = lodash.get(v, 'management.keys.address', '')
23+ const spawn = lodash.get(v, 'spawn.keys.address', '');
24− const voting = lodash.get(v, 'voting.keys.address', '')
24+ const manage = lodash.get(v, 'management.keys.address', '');
25− const auth = lodash.get(v, 'network.keys.auth.public', '')
25+ const voting = lodash.get(v, 'voting.keys.address', '');
26− const crypt = lodash.get(v, 'network.keys.crypt.public', '')
26+ const auth = lodash.get(v, 'network.keys.auth.public', '');
27− return {
27+ const crypt = lodash.get(v, 'network.keys.crypt.public', '');
28− ...acc,
28+ return {
29− [k]: [owner, transfer, spawn, manage, voting, auth, crypt],
29+ ...acc,
30− }
30+ [k]: [owner, transfer, spawn, manage, voting, auth, crypt],
31−
31+ };
32− }, { idCode: profile.idCode });
32+ },
33+ { idCode: profile.idCode }
34+ );
33
35
34 const json_urbit_registration = JSON.stringify(urbit_registration);
36 const json_urbit_registration = JSON.stringify(urbit_registration);
35 const encoded_urbit_registration = new Blob(
37 const encoded_urbit_registration = new Blob(
36 [btoa(json_urbit_registration)],
38 [btoa(json_urbit_registration)],
37− {type: "text/plain;charset=utf-8"}
39+ { type: 'text/plain;charset=utf-8' }
38 );
40 );
39
41
40 const zip = new JSZip();
42 const zip = new JSZip();
@@ -45,13 +47,13 @@ class Download extends React.Component {
45 const bin3 = paper.filter(item => item.bin === '3');
47 const bin3 = paper.filter(item => item.bin === '3');
46 const bin4 = paper.filter(item => item.bin === '4');
48 const bin4 = paper.filter(item => item.bin === '4');
47
49
48− const bin0Folder = zip.folder("0. Public");
50+ const bin0Folder = zip.folder('0. Public');
49− const bin1Folder = zip.folder("1. Very High Friction Custody");
51+ const bin1Folder = zip.folder('1. Very High Friction Custody');
50− const bin2Folder = zip.folder("2. High Friction Custody");
52+ const bin2Folder = zip.folder('2. High Friction Custody');
51− const bin3Folder = zip.folder("3. Medium Friction Custody");
53+ const bin3Folder = zip.folder('3. Medium Friction Custody');
52− const bin4Folder = zip.folder("4. Low Friction Custody");
54+ const bin4Folder = zip.folder('4. Low Friction Custody');
53
55
54− bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png))
56+ bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
55 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
57 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
56 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
58 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
57 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
59 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
@@ -61,87 +63,106 @@ class Download extends React.Component {
61 // const jsonWallet = JSON.stringify(wallets, null, 2);
63 // const jsonWallet = JSON.stringify(wallets, null, 2);
62 // zip.file("jsonWallet.json", jsonWallet);
64 // zip.file("jsonWallet.json", jsonWallet);
63
65
64− zip.generateAsync({type:"blob"})
66+ zip.generateAsync({ type: 'blob' }).then(content => {
65− .then((content) => {
67+ saveAs(content, 'urbit-wallets.zip');
66− saveAs(content, 'urbit-wallets.zip')
67 setTimeout(() => {
68 setTimeout(() => {
68 saveAs(encoded_urbit_registration, 'urbit-registration.txt');
69 saveAs(encoded_urbit_registration, 'urbit-registration.txt');
69 props.setGlobalState({ didClickDownload: true });
70 props.setGlobalState({ didClickDownload: true });
70 }, 2000);
71 }, 2000);
71 });
72 });
72−
73+ };
73− }
74
74
75 render() {
75 render() {
76+ const { setGlobalState } = this.props;
76
77
77− const { setGlobalState } = this.props
78+ const { props } = this;
78−
79− const { props } = this
80
79
81− const downloaded = props['downloaded']
80+ const downloaded = props['downloaded'];
82− const didClickDownload = props['didClickDownload']
81+ const didClickDownload = props['didClickDownload'];
83
82
84− const nextButtonClass = downloaded ? 'btn btn-primary' : 'btn shape-gray-10'
83+ const nextButtonClass = downloaded
84+ ? 'btn btn-primary'
85+ : 'btn shape-gray-10';
85
86
86− return(
87+ return (
87 <div className={'col-md-6'}>
88 <div className={'col-md-6'}>
88− <h2 className={'mb-4'}>Your wallets have been generated. Download the files to a storage medium you trust.</h2>
89+ <h2 className={'mb-4'}>
89−
90+ Your wallets have been generated. Download the files to a storage
91+ medium you trust.
92+ </h2>
90
93
91 <div
94 <div
92− className={ `white row text-mono pb-2` }
95+ className={`white row text-mono pb-2`}
93− style={{borderBottom:'2px solid #222' }}
96+ style={{ borderBottom: '2px solid #222' }}>
94− >
95 <div className={'col-md-6 text-400'}>{'Filename'}</div>
97 <div className={'col-md-6 text-400'}>{'Filename'}</div>
96 <div className={'col-md-6 text-400'}>{'Contents'}</div>
98 <div className={'col-md-6 text-400'}>{'Contents'}</div>
97 </div>
99 </div>
98
100
99−
100 <div
101 <div
101− className={ `white row pv-4` }
102+ className={`white row pv-4`}
102− style={{borderBottom:'2px solid #222' }}
103+ style={{ borderBottom: '2px solid #222' }}>
103− >
104+ <div className={'col-md-6 text-mono text-600'}>
104− <div className={'col-md-6 text-mono text-600'}>{'urbit-wallets.zip'}</div>
105+ {'urbit-wallets.zip'}
105− <div className={'col-md-6'}><p className={'mv-0'}>Paper wallet PNG files for <code>{`${props['profile.shipCount']}`}</code> ships, organized by recommended custody tier. This contains your private keys, keep it safe. </p></div>
106+ </div>
107+ <div className={'col-md-6'}>
108+ <p className={'mv-0'}>
109+ Paper wallet PNG files for{' '}
110+ <code>{`${props['profile.shipCount']}`}</code> ships, organized by
111+ recommended custody tier. This contains your private keys, keep it
112+ safe.{' '}
113+ </p>
114+ </div>
106 </div>
115 </div>
107
116
108−
109 <div
117 <div
110− className={ `white row pv-2` }
118+ className={`white row pv-2`}
111− style={{borderBottom:'2px solid #222' }}
119+ style={{ borderBottom: '2px solid #222' }}>
112− >
120+ <div className={'col-md-6 text-600 text-mono'}>
113− <div className={'col-md-6 text-600 text-mono'}>{'urbit-registration.txt'}</div>
121+ {'urbit-registration.txt'}
114− <div className={'col-md-6 mv-0'}><p className={'mv-0'}>{`A file containing the public addresses of your wallets. You need to upload this to Registration next.`}</p></div>
122+ </div>
123+ <div className={'col-md-6 mv-0'}>
124+ <p
125+ className={
126+ 'mv-0'
127+ }>{`A file containing the public addresses of your wallets. You need to upload this to Registration next.`}</p>
128+ </div>
115 </div>
129 </div>
116
130
117 <Button
131 <Button
118 className={'btn btn-primary mv-6'}
132 className={'btn btn-primary mv-6'}
119 text={'Download All'}
133 text={'Download All'}
120− onClick={ e => this.handleSave(e) }
134+ onClick={e => this.handleSave(e)}
121 />
135 />
122
136
123− {
137+ {didClickDownload ? (
124− didClickDownload
138+ <div>
125− ? <div>
139+ <p>
126− <p>Please verify that the above files have been successfully downloaded. You may wish to unpack the zip file and inspect the contents. If you open your .png wallets on your screen, ensure you are in a secure location.</p>
140+ Please verify that the above files have been successfully
127− <CheckBox
141+ downloaded. You may wish to unpack the zip file and inspect the
128− title={'I have my urbit-wallets.zip and urbit-registration.txt'}
142+ contents. If you open your .png wallets on your screen, ensure you
129− onChange={ () => setGlobalState({ downloaded: !downloaded }) }
143+ are in a secure location.
130− state={ downloaded }
144+ </p>
131− />
145+ <CheckBox
132−
146+ title={'I have my urbit-wallets.zip and urbit-registration.txt'}
133− <Button
147+ onChange={() => setGlobalState({ downloaded: !downloaded })}
134− className={ nextButtonClass + ' mt-8' }
148+ state={downloaded}
135− text={'Next →'}
149+ />
136− disabled={ !downloaded }
150+
137− onClick= { () => setGlobalState({ route: '/Done' , 'currentStep': NEXT_STEP_NUM })}
151+ <Button
138− />
152+ className={nextButtonClass + ' mt-8'}
139− </div>
153+ text={'Next →'}
140− : <div />
154+ disabled={!downloaded}
141− }
155+ onClick={() =>
156+ setGlobalState({ route: '/Done', currentStep: NEXT_STEP_NUM })
157+ }
158+ />
159+ </div>
160+ ) : (
161+ <div />
162+ )}
142 </div>
163 </div>
143− )
164+ );
144 }
165 }
145}
166}
146
167
147−export default Download
168+export default Download;
src/walletgen/views/Eula.js+21−22
@@ -1,10 +1,9 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import EulaText from '../components/EulaText'
3+import EulaText from '../components/EulaText';
4−import Button from '../components/Button'
4+import Button from '../components/Button';
5−
6−const NEXT_STEP_NUM = 2
7
5
6+const NEXT_STEP_NUM = 2;
8
7
9class Eula extends React.Component {
8class Eula extends React.Component {
10 render() {
9 render() {
@@ -12,38 +11,38 @@ class Eula extends React.Component {
12
11
13 return (
12 return (
14 <div>
13 <div>
15−
16 <EulaText />
14 <EulaText />
17
15
18 <div className={'mt-8'}>
16 <div className={'mt-8'}>
19−
20 <Button
17 <Button
21 className={'btn btn-primary mb-4'}
18 className={'btn btn-primary mb-4'}
22 text={'I Accept'}
19 text={'I Accept'}
23− onClick={ () => setGlobalState({
20+ onClick={() =>
24− 'route': '/Upload',
21+ setGlobalState({
25− 'currentStep': NEXT_STEP_NUM,
22+ route: '/Upload',
26− })}
23+ currentStep: NEXT_STEP_NUM,
24+ })
25+ }
27 />
26 />
28
27
29 <Button
28 <Button
30 className={'btn shape-gray-10 ml-2'}
29 className={'btn shape-gray-10 ml-2'}
31 text={'I Do Not Accept'}
30 text={'I Do Not Accept'}
32− onClick={ () => setGlobalState({ 'route': '/Welcome' })}
31+ onClick={() => setGlobalState({ route: '/Welcome' })}
33 />
32 />
34−
35 </div>
33 </div>
36
34
37− <p><strong>
35+ <p>
38− By clicking “I Accept,” or by downloading, installing, or otherwise
36+ <strong>
39− accessing or using the Software, you agree that you have read and
37+ By clicking “I Accept,” or by downloading, installing, or otherwise
40− understood, and, as a condition to your use of the Software, you agree
38+ accessing or using the Software, you agree that you have read and
41− to be bound by, the above terms and conditions.
39+ understood, and, as a condition to your use of the Software, you
42− </strong></p>
40+ agree to be bound by, the above terms and conditions.
43−
41+ </strong>
42+ </p>
44 </div>
43 </div>
45− )
44+ );
46 }
45 }
47}
46}
48
47
49−export default Eula
48+export default Eula;
src/walletgen/views/EulaView.js+14−16
@@ -1,7 +1,7 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import EulaText from '../components/EulaText'
3+import EulaText from '../components/EulaText';
4−import Button from '../components/Button'
4+import Button from '../components/Button';
5
5
6class EulaView extends React.Component {
6class EulaView extends React.Component {
7 render() {
7 render() {
@@ -9,29 +9,27 @@ class EulaView extends React.Component {
9
9
10 return (
10 return (
11 <div>
11 <div>
12−
13 <EulaText />
12 <EulaText />
14
13
15 <div className={'mt-8'}>
14 <div className={'mt-8'}>
16−
17 <Button
15 <Button
18 className={'btn shape-gray-10 ml-2'}
16 className={'btn shape-gray-10 ml-2'}
19 text={'Close'}
17 text={'Close'}
20− onClick={ () => setGlobalState({ 'route': lastRoute })}
18+ onClick={() => setGlobalState({ route: lastRoute })}
21 />
19 />
22−
23 </div>
20 </div>
24
21
25− <p><strong>
22+ <p>
26− By clicking “I Accept,” or by downloading, installing, or otherwise
23+ <strong>
27− accessing or using the Software, you agree that you have read and
24+ By clicking “I Accept,” or by downloading, installing, or otherwise
28− understood, and, as a condition to your use of the Software, you agree
25+ accessing or using the Software, you agree that you have read and
29− to be bound by, the above terms and conditions.
26+ understood, and, as a condition to your use of the Software, you
30− </strong></p>
27+ agree to be bound by, the above terms and conditions.
31−
28+ </strong>
29+ </p>
32 </div>
30 </div>
33− )
31+ );
34 }
32 }
35}
33}
36
34
37−export default EulaView
35+export default EulaView;
src/walletgen/views/Generate.js+221−178
@@ -1,46 +1,46 @@
1−import React from 'react'
1+import React from 'react';
2−import * as sigil from 'sigil-js'
2+import * as sigil from 'sigil-js';
3−import ob from 'urbit-ob'
3+import ob from 'urbit-ob';
4−import classnames from 'classnames'
4+import classnames from 'classnames';
5−import * as wg from '../lib/lib'
5+import * as wg from '../lib/lib';
6
6
7+import Button from '../components/Button';
8+import ReactSVGComponents from '../components/ReactSVGComponents';
9+import PaperCollateralRenderer from 'PaperCollateralRenderer';
7
10
8−import Button from '../components/Button'
11+import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants';
9−import ReactSVGComponents from '../components/ReactSVGComponents'
10−import PaperCollateralRenderer from 'PaperCollateralRenderer'
11−
12−import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants'
13
12
14const NEXT_STEP_NUM = 6;
13const NEXT_STEP_NUM = 6;
15
14
16const shipInfo = ship => {
15const shipInfo = ship => {
17− const name = ob.patp(ship)
16+ const name = ob.patp(ship);
18 const sig = sigil.pour({
17 const sig = sigil.pour({
19 colorway: ['black', 'white'],
18 colorway: ['black', 'white'],
20 patp: name,
19 patp: name,
21 renderer: ReactSVGComponents,
20 renderer: ReactSVGComponents,
22− size: 233
21+ size: 233,
23− })
22+ });
24
23
25 return ship < MAX_GALAXY
24 return ship < MAX_GALAXY
26 ? galaxyInfo(name, sig)
25 ? galaxyInfo(name, sig)
27 : ship < MAX_STAR
26 : ship < MAX_STAR
28 ? starInfo(name, sig)
27 ? starInfo(name, sig)
29− : planetInfo(name, sig)
28+ : planetInfo(name, sig);
30−}
29+};
31
30
32−const infoHeader = ship =>
31+const infoHeader = ship => (
33− <div className={'row text-600 text-mono'} key={ ship + '-header' }>
32+ <div className={'row text-600 text-mono'} key={ship + '-header'}>
34 {ship + ' Wallet'}
33 {ship + ' Wallet'}
35 </div>
34 </div>
35+);
36
36
37const infoSigil = (ship, sig) => {
37const infoSigil = (ship, sig) => {
38 return (
38 return (
39− <div className={ '' } key={ 'sigil-' + ship }>
39+ <div className={''} key={'sigil-' + ship}>
40− { sig }
40+ {sig}
41 </div>
41 </div>
42− )
42+ );
43−}
43+};
44
44
45const getColor = (classOf, seedType) => {
45const getColor = (classOf, seedType) => {
46 if (classOf === 'galaxy') {
46 if (classOf === 'galaxy') {
@@ -59,110 +59,142 @@ const getColor = (classOf, seedType) => {
59 if (seedType === 'manage') return 'bg-low';
59 if (seedType === 'manage') return 'bg-low';
60 if (seedType === 'master') return 'bg-medium';
60 if (seedType === 'master') return 'bg-medium';
61 }
61 }
62−
62+};
63−}
64−
65−
66
63
67const infoManagementSeed = ship => {
64const infoManagementSeed = ship => {
68− const classOf = ob.clan(ship)
65+ const classOf = ob.clan(ship);
69− const colorClass = getColor(classOf, 'manage')
66+ const colorClass = getColor(classOf, 'manage');
70− return <Tile colorClass={colorClass} key={`info-${ship}-manage`} col1={`Management Proxy`} col2={''} />
67+ return (
71−
68+ <Tile
72−}
69+ colorClass={colorClass}
70+ key={`info-${ship}-manage`}
71+ col1={`Management Proxy`}
72+ col2={''}
73+ />
74+ );
75+};
73
76
74const infoVotingSeed = ship => {
77const infoVotingSeed = ship => {
75− const classOf = ob.clan(ship)
78+ const classOf = ob.clan(ship);
76− const colorClass = getColor(classOf, 'voting')
79+ const colorClass = getColor(classOf, 'voting');
77− return <Tile colorClass={colorClass} key={`info-${ship}-voting`} col1={`Voting Proxy`} col2={''} />
80+ return (
78−}
81+ <Tile
79−
82+ colorClass={colorClass}
80−
83+ key={`info-${ship}-voting`}
84+ col1={`Voting Proxy`}
85+ col2={''}
86+ />
87+ );
88+};
81
89
82const infoSpawnSeed = ship => {
90const infoSpawnSeed = ship => {
83− const classOf = ob.clan(ship)
91+ const classOf = ob.clan(ship);
84− const colorClass = getColor(classOf, 'spawn')
92+ const colorClass = getColor(classOf, 'spawn');
85− return <Tile colorClass={colorClass} key={`info-${ship}-spawn`} col1={`Spawn Proxy`} col2={''} />
93+ return (
86−}
94+ <Tile
87−
95+ colorClass={colorClass}
88−
96+ key={`info-${ship}-spawn`}
97+ col1={`Spawn Proxy`}
98+ col2={''}
99+ />
100+ );
101+};
89
102
90const infoMasterTicket = ship => {
103const infoMasterTicket = ship => {
91 const classOf = ob.clan(ship);
104 const classOf = ob.clan(ship);
92 const colorClass = getColor(classOf, 'master');
105 const colorClass = getColor(classOf, 'master');
93− return <Tile colorClass={colorClass} key={`info-${ship}-master`} col1={`Master Ticket`} col2={''} />;
106+ return (
107+ <Tile
108+ colorClass={colorClass}
109+ key={`info-${ship}-master`}
110+ col1={`Master Ticket`}
111+ col2={''}
112+ />
113+ );
94};
114};
95
115
96−
97−
98const infoMasterTicketShard = ship => {
116const infoMasterTicketShard = ship => {
99 const classOf = ob.clan(ship);
117 const classOf = ob.clan(ship);
100 const colorClass = getColor(classOf, 'master');
118 const colorClass = getColor(classOf, 'master');
101 return [
119 return [
102− <Tile colorClass={colorClass} key={`info-${ship}-master-1`} col1={`Master Ticket Shard 1 of 3`} col2={''} />,
120+ <Tile
103− <Tile colorClass={colorClass} key={`info-${ship}-master-2`} col1={`Master Ticket Shard 2 of 3`} col2={''} />,
121+ colorClass={colorClass}
104− <Tile colorClass={colorClass} key={`info-${ship}-master-3`} col1={`Master Ticket Shard 3 of 3`} col2={''} />,
122+ key={`info-${ship}-master-1`}
123+ col1={`Master Ticket Shard 1 of 3`}
124+ col2={''}
125+ />,
126+ <Tile
127+ colorClass={colorClass}
128+ key={`info-${ship}-master-2`}
129+ col1={`Master Ticket Shard 2 of 3`}
130+ col2={''}
131+ />,
132+ <Tile
133+ colorClass={colorClass}
134+ key={`info-${ship}-master-3`}
135+ col1={`Master Ticket Shard 3 of 3`}
136+ col2={''}
137+ />,
105 ];
138 ];
106};
139};
107
140
108−
141+const Tile = props => {
109−
110−const Tile = (props) => {
111 return (
142 return (
112 <div
143 <div
113− className={ `${props.colorClass} white row pl-4 text-mono` }
144+ className={`${props.colorClass} white row pl-4 text-mono`}
114− style={{borderBottom:'2px solid black', height: '39px' }}
145+ style={{ borderBottom: '2px solid black', height: '39px' }}
115 key={props.key}>
146 key={props.key}>
116−
117 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
147 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
118 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
148 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
119 </div>
149 </div>
120− )
150+ );
121−}
151+};
122−
123
152
124−const galaxyInfo = (ship, sig) =>
153+const galaxyInfo = (ship, sig) => (
125− <div className={'mt-8'} key={ 'wallet-' + ship }>
154+ <div className={'mt-8'} key={'wallet-' + ship}>
126− { infoHeader(ship) }
155+ {infoHeader(ship)}
127− <div className={'row'} key={ ship + '-body' }>
156+ <div className={'row'} key={ship + '-body'}>
128− { infoSigil(ship, sig) }
157+ {infoSigil(ship, sig)}
129− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
158+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
130− { infoMasterTicketShard(ship) }
159+ {infoMasterTicketShard(ship)}
131− { infoSpawnSeed(ship) }
160+ {infoSpawnSeed(ship)}
132− { infoManagementSeed(ship) }
161+ {infoManagementSeed(ship)}
133− { infoVotingSeed(ship) }
162+ {infoVotingSeed(ship)}
134 </div>
163 </div>
135 </div>
164 </div>
136 </div>
165 </div>
137−
166+);
138−const starInfo = (ship, sig) =>
167+
139− <div className={'mt-8'} key={ 'wallet-' + ship }>
168+const starInfo = (ship, sig) => (
140− { infoHeader(ship) }
169+ <div className={'mt-8'} key={'wallet-' + ship}>
141− <div className={'row'} key={ ship + '-body' }>
170+ {infoHeader(ship)}
142− { infoSigil(ship, sig) }
171+ <div className={'row'} key={ship + '-body'}>
143− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
172+ {infoSigil(ship, sig)}
144− { infoMasterTicket(ship) }
173+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
145− { infoSpawnSeed(ship) }
174+ {infoMasterTicket(ship)}
146− { infoManagementSeed(ship) }
175+ {infoSpawnSeed(ship)}
176+ {infoManagementSeed(ship)}
147 </div>
177 </div>
148 </div>
178 </div>
149 </div>
179 </div>
150−
180+);
151−const planetInfo = (ship, sig) =>
181+
152− <div className={'mt-8'} key={ 'wallet-' + ship }>
182+const planetInfo = (ship, sig) => (
153− { infoHeader(ship) }
183+ <div className={'mt-8'} key={'wallet-' + ship}>
154− <div className={'row'} key={ ship + '-body' }>
184+ {infoHeader(ship)}
155− { infoSigil(ship, sig) }
185+ <div className={'row'} key={ship + '-body'}>
156− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
186+ {infoSigil(ship, sig)}
157− { infoMasterTicket(ship) }
187+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
158− { infoManagementSeed(ship) }
188+ {infoMasterTicket(ship)}
189+ {infoManagementSeed(ship)}
159 </div>
190 </div>
160 </div>
191 </div>
161 </div>
192 </div>
193+);
162
194
163class Generate extends React.Component {
195class Generate extends React.Component {
164 constructor(props) {
196 constructor(props) {
165− super(props)
197+ super(props);
166 this.handleGenerate = this.handleGenerate.bind(this);
198 this.handleGenerate = this.handleGenerate.bind(this);
167 }
199 }
168
200
@@ -205,16 +237,17 @@ class Generate extends React.Component {
205
237
206 const wallets = await Promise.all(walletPromises);
238 const wallets = await Promise.all(walletPromises);
207
239
208− const reshapedWallet = wallets.reduce((acc, {ship, wallet}) => ({...acc, [ship]: wallet }), {})
240+ const reshapedWallet = wallets.reduce(
241+ (acc, { ship, wallet }) => ({ ...acc, [ship]: wallet }),
242+ {}
243+ );
209
244
210 setGlobalState({
245 setGlobalState({
211 'wallets.value': reshapedWallet,
246 'wallets.value': reshapedWallet,
212 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
247 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
213 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
248 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
214 });
249 });
215− }
250+ };
216−
217−
218
251
219 forciblyExecuteArgon2Once = async enyTable => {
252 forciblyExecuteArgon2Once = async enyTable => {
220 // NB (jtobin):
253 // NB (jtobin):
@@ -222,15 +255,17 @@ class Generate extends React.Component {
222 // following is some insane thing required so as not to run into bizarre
255 // following is some insane thing required so as not to run into bizarre
223 // emscripten errors
256 // emscripten errors
224 if (enyTable.length === 0) {
257 if (enyTable.length === 0) {
225− throw new Error('no ship information provided')
258+ throw new Error('no ship information provided');
226 } else {
259 } else {
227− let throwawayEntry = enyTable[0]
260+ let throwawayEntry = enyTable[0];
228 // eslint-disable-next-line
261 // eslint-disable-next-line
229− let throwaway = await genWallet(throwawayEntry.ship, throwawayEntry.ticket, () => {})
262+ let throwaway = await genWallet(
230− };
263+ throwawayEntry.ship,
231− }
264+ throwawayEntry.ticket,
232−
265+ () => {}
233−
266+ );
267+ }
268+ };
234
269
235 render() {
270 render() {
236 const { props } = this;
271 const { props } = this;
@@ -255,26 +290,17 @@ class Generate extends React.Component {
255 const ws = props['wallets.state'];
290 const ws = props['wallets.state'];
256 const ps = props['paperCollateral.state'];
291 const ps = props['paperCollateral.state'];
257
292
258−
259 const ALL_SUCCESS =
293 const ALL_SUCCESS =
260− es === ENY_SUCCESS &&
294+ es === ENY_SUCCESS && ws === DERIVATION_SUCCESS && ps === PAPER_SUCCESS;
261− ws === DERIVATION_SUCCESS &&
262− ps === PAPER_SUCCESS;
263
295
264 const NONE_START =
296 const NONE_START =
265− es === ENY_NOSTART &&
297+ es === ENY_NOSTART && ws === DERIVATION_NOSTART && ps === PAPER_NOSTART;
266− ws === DERIVATION_NOSTART &&
267− ps === PAPER_NOSTART;
268
298
269 const ANY_ERROR =
299 const ANY_ERROR =
270− es === ENY_FAILURE ||
300+ es === ENY_FAILURE || ws === DERIVATION_FAILURE || ps === PAPER_FAILURE;
271− ws === DERIVATION_FAILURE ||
272− ps === PAPER_FAILURE;
273
301
274 const ANY_PENDING =
302 const ANY_PENDING =
275− es === ENY_PENDING ||
303+ es === ENY_PENDING || ws === DERIVATION_PENDING || ps === PAPER_PENDING;
276− ws === DERIVATION_PENDING ||
277− ps === PAPER_PENDING;
278
304
279 const genButtonClass = classnames({
305 const genButtonClass = classnames({
280 'bg-blue white': true,
306 'bg-blue white': true,
@@ -324,7 +350,7 @@ class Generate extends React.Component {
324 if (ALL_SUCCESS === true) r = 'Success.';
350 if (ALL_SUCCESS === true) r = 'Success.';
325 if (ANY_ERROR === true) r = 'Try Again';
351 if (ANY_ERROR === true) r = 'Try Again';
326 return r;
352 return r;
327− }
353+ };
328
354
329 const disableGenerateButton = isGenerateButtonDisabled();
355 const disableGenerateButton = isGenerateButtonDisabled();
330 const genText = getGenButtonText();
356 const genText = getGenButtonText();
@@ -332,72 +358,86 @@ class Generate extends React.Component {
332 const nextButtonClass = classnames({
358 const nextButtonClass = classnames({
333 'bg-blue white': ALL_SUCCESS === true,
359 'bg-blue white': ALL_SUCCESS === true,
334 'bg-gray black': ALL_SUCCESS !== true,
360 'bg-gray black': ALL_SUCCESS !== true,
335− })
361+ });
336
362
337 const nextButtonDisabled = ws !== ALL_SUCCESS;
363 const nextButtonDisabled = ws !== ALL_SUCCESS;
338
364
339− return(
365+ return (
340 <div className={'row'}>
366 <div className={'row'}>
341− <div className={'col-md-8'}>
342 <div className={'col-md-8'}>
367 <div className={'col-md-8'}>
343− <h2>{`Review your paper wallet storage recommendations.`}</h2>
368+ <div className={'col-md-8'}>
344− <p>
369+ <h2>{`Review your paper wallet storage recommendations.`}</h2>
345− {`Each ship is placed into its own wallet with proxies that differ in
370+ <p>
371+ {`Each ship is placed into its own wallet with proxies that differ in
346 importance and security requirements. These are listed as examples below. A key to our security recommendations can be found to the right of the page.`}
372 importance and security requirements. These are listed as examples below. A key to our security recommendations can be found to the right of the page.`}
347− </p>
373+ </p>
348− </div>
374+ </div>
349
375
350− { props['profile.value'].galaxies.map(ship => shipInfo(ship)) }
376+ {props['profile.value'].galaxies.map(ship => shipInfo(ship))}
351− { props['profile.value'].stars.map(ship => shipInfo(ship)) }
377+ {props['profile.value'].stars.map(ship => shipInfo(ship))}
352− { props['profile.value'].planets.map(ship => shipInfo(ship)) }
378+ {props['profile.value'].planets.map(ship => shipInfo(ship))}
379+ </div>
353
380
354− </div>
381+ <div className="col-md-3 col-md-offset-1 mt-9">
382+ <div className="fixed" style={{ width: '230px' }}>
383+ <div className="col-md-12 bg-very-high h-2 mt-4"></div>
384+ <h4 className="text-600 mt-2">Very High Friction</h4>
385+ <p className="text-sm" style={{ lineHeight: '16px' }}>
386+ Extremely valuable. Cold storage on a 3 part sharded paper wallet.
387+ Store each shard in a separate geographic location with protection
388+ against fire, water and other elements.
389+ </p>
390+ <div className="col-md-12 bg-high h-2 mt-4"></div>
391+ <h4 className="text-600 mt-2">High Friction</h4>
392+ <p className="text-sm" style={{ lineHeight: '16px' }}>
393+ Very valuable. Cold storage on a paper wallet in a different
394+ location than your computer, such as a safe.
395+ </p>
396+ <div className="col-md-12 bg-medium h-2 mt-4"></div>
397+ <h4 className="text-600 mt-2">Medium Friction</h4>
398+ <p className="text-sm" style={{ lineHeight: '16px' }}>
399+ Store cold in a paper wallet; can be stored in a desk drawer in a
400+ trusted environment.
401+ </p>
402+ <div className="col-md-12 bg-low h-2 mt-4"></div>
403+ <h4 className="text-600 mt-2">Low Friction</h4>
404+ <p className="text-sm" style={{ lineHeight: '16px' }}>
405+ Ok to store hot on a trusted networked computer.
406+ </p>
407+ </div>
408+ </div>
355
409
356− <div className="col-md-3 col-md-offset-1 mt-9">
410+ <div className={'col-md-9 btn-tray col-'}>
357− <div className="fixed" style={{width: '230px'}}>
411+ <h3>{"Got it? Then it's time to generate your wallets."}</h3>
358− <div className="col-md-12 bg-very-high h-2 mt-4"></div>
412+ <p className={'col-md-8'}>
359− <h4 className="text-600 mt-2">Very High Friction</h4>
413+ {
360− <p className="text-sm" style={{lineHeight: '16px'}}>
414+ 'If you have a large number of ships, this may take several minutes and the page may hang or become unresponsive. If you close this browser window, you will have to start from the beginning.'
361− Extremely valuable. Cold storage on a 3 part sharded paper wallet. Store each shard in a separate geographic location with protection against fire, water and other elements.
415+ }
362 </p>
416 </p>
363− <div className="col-md-12 bg-high h-2 mt-4"></div>
364− <h4 className="text-600 mt-2">High Friction</h4>
365− <p className="text-sm" style={{lineHeight: '16px'}}>
366− Very valuable. Cold storage on a paper wallet in a different location than your computer, such as a safe.
367− </p>
368− <div className="col-md-12 bg-medium h-2 mt-4"></div>
369− <h4 className="text-600 mt-2">Medium Friction</h4>
370− <p className="text-sm" style={{lineHeight: '16px'}}>
371− Store cold in a paper wallet; can be stored in a desk drawer in a trusted environment.
372− </p>
373− <div className="col-md-12 bg-low h-2 mt-4"></div>
374− <h4 className="text-600 mt-2">Low Friction</h4>
375− <p className="text-sm" style={{lineHeight: '16px'}}>
376− Ok to store hot on a trusted networked computer.
377− </p>
378− </div>
379− </div>
380
417
381− <div className={'col-md-9 btn-tray col-'}>
418+ <div className={'flex h-12 mt-8 mb-4'}>
382− <h3>{"Got it? Then it's time to generate your wallets."}</h3>
419+ <Button
383− <p className={'col-md-8'}>{"If you have a large number of ships, this may take several minutes and the page may hang or become unresponsive. If you close this browser window, you will have to start from the beginning."}</p>
420+ className={`${genButtonClass} b-r-1-black w-50 m-0`}
384−
421+ text={genText}
385− <div className={'flex h-12 mt-8 mb-4'}>
422+ onClick={this.handleGenerate}
386− <Button
423+ disable={disableGenerateButton}
387− className={ `${genButtonClass} b-r-1-black w-50 m-0` }
424+ />
388− text={genText}
425+ <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
389− onClick={ this.handleGenerate }
426+ <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
390− disable={disableGenerateButton} />
427+ <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
391− <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
428+ <Button
392− <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
429+ className={`${nextButtonClass} m-0`}
393− <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
430+ text={'View wallets →'}
394− <Button
431+ disable={nextButtonDisabled}
395− className={ `${nextButtonClass} m-0` }
432+ onClick={() =>
396− text={'View wallets →'}
433+ setGlobalState({
397− disable={ nextButtonDisabled }
434+ route: '/Download',
398− onClick= { () => setGlobalState({ route: '/Download' , 'currentStep': NEXT_STEP_NUM })} />
435+ currentStep: NEXT_STEP_NUM,
436+ })
437+ }
438+ />
439+ </div>
399 </div>
440 </div>
400− </div>
401
441
402 <div className={'extremely-hidden'}>
442 <div className={'extremely-hidden'}>
403 <div className={'text-300'}>{'text-300'}</div>
443 <div className={'text-300'}>{'text-300'}</div>
@@ -410,21 +450,24 @@ class Generate extends React.Component {
410 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
450 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
411 </div>
451 </div>
412
452
413− { props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS
453+ {props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS ? (
414− ? <PaperCollateralRenderer
454+ <PaperCollateralRenderer
415 wallet={props['wallets.value']}
455 wallet={props['wallets.value']}
416 className={'extremely-hidden'}
456 className={'extremely-hidden'}
417− callback={data => setGlobalState({
457+ callback={data =>
418− 'paperCollateral.value': data,
458+ setGlobalState({
419− 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS
459+ 'paperCollateral.value': data,
420− })}
460+ 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS,
421− mode={'REGISTRATION'} />
461+ })
422− : <div />
462+ }
423− }
463+ mode={'REGISTRATION'}
424−
464+ />
465+ ) : (
466+ <div />
467+ )}
425 </div>
468 </div>
426− )
469+ );
427 }
470 }
428}
471}
429
472
430−export default Generate
473+export default Generate;
src/walletgen/views/NetworkWarning.js+6−6
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−const NetworkWarning = (props) =>
3+const NetworkWarning = props => (
4 <div className={'col-md-6'}>
4 <div className={'col-md-6'}>
5 <h2>{'You may not use this tool while connected to a network.'}</h2>
5 <h2>{'You may not use this tool while connected to a network.'}</h2>
6
6
7 <p>
7 <p>
8− To continue, disconnect from the internet and refresh the page. If you highly value your ships,
8+ To continue, disconnect from the internet and refresh the page. If you
9− we recommend you use a new, airgapped machine.
9+ highly value your ships, we recommend you use a new, airgapped machine.
10 </p>
10 </p>
11−
12 </div>
11 </div>
12+);
13
13
14−export default NetworkWarning
14+export default NetworkWarning;
src/walletgen/views/PrivacyPolicy.js+229−305
@@ -1,443 +1,367 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import Button from '../components/Button'
3+import Button from '../components/Button';
4
4
5class PrivacyPolicy extends React.Component {
5class PrivacyPolicy extends React.Component {
6−
7 render() {
6 render() {
8− const { setGlobalState, lastRoute } = this.props
7+ const { setGlobalState, lastRoute } = this.props;
9
8
10 return (
9 return (
11 <div>
10 <div>
11+ <p>Effective date: June 01, 2018</p>
12 <p>
12 <p>
13− Effective date: June 01, 2018
13+ Tlon Corporation ("us", "we", or "our") operates the website and the
14− </p>
14+ Urbit mobile application (the "Service").
15− <p>
16− Tlon Corporation ("us", "we", or "our") operates the website and the Urbit
17− mobile application (the "Service").
18− </p>
19− <p>
20− This page informs you of our policies regarding the collection, use, and
21− disclosure of personal data when you use our Service and the choices you have
22− associated with that data.
23− </p>
24− <p>
25− We use your data to provide and improve the Service. By using the Service, you
26− agree to the collection and use of information in accordance with this policy.
27− Unless otherwise defined in this Privacy Policy, terms used in this Privacy
28− Policy have the same meanings as in our Terms and Conditions.
29− </p>
30− <p>
31− Definitions
32− </p>
33− <p>
34− Service
35− </p>
36− <p>
37− Service means the website and the Urbit mobile application operated by Tlon
38− Corporation
39− </p>
40− <p>
41− Personal Data
42− </p>
43− <p>
44− Personal Data means data about a living individual who can be identified from
45− those data (or from those and other information either in our possession or
46− likely to come into our possession).
47− </p>
48− <p>
49− Usage Data
50− </p>
51− <p>
52− Usage Data is data collected automatically either generated by the use of the
53− Service or from the Service infrastructure itself (for example, the duration of
54− a page visit).
55− </p>
56− <p>
57− Cookies
58− </p>
59− <p>
60− Cookies are small pieces of data stored on your device (computer or mobile
61− device).
62− </p>
63− <p>
64− Data Controller
65− </p>
66− <p>
67− Data Controller means the natural or legal person who (either alone or jointly
68− or in common with other persons) determines the purposes for which and the
69− manner in which any personal information are, or are to be, processed.
70− </p>
71− <p>
72− For the purpose of this Privacy Policy, we are a Data Controller of your
73− Personal Data.
74− </p>
75− <p>
76− Data Processors (or Service Providers)
77− </p>
78− <p>
79− Data Processor (or Service Provider) means any natural or legal person who
80− processes the data on behalf of the Data Controller.
81− </p>
82− <p>
83− We may use the services of various Service Providers in order to process your
84− data more effectively.
85− </p>
86− <p>
87− Data Subject (or User)
88− </p>
89− <p>
90− Data Subject is any living individual who is using our Service and is the
91− subject of Personal Data.
92− </p>
93− <p>
94− Information Collection and Use
95− </p>
96− <p>
97− We collect several different types of information for various purposes to
98− provide and improve our Service to you.
99− </p>
100− <p>
101− Types of Data Collected
102 </p>
15 </p>
103 <p>
16 <p>
104− Personal Data
17+ This page informs you of our policies regarding the collection, use,
18+ and disclosure of personal data when you use our Service and the
19+ choices you have associated with that data.
105 </p>
20 </p>
106 <p>
21 <p>
107− While using our Service, we may ask you to provide us with certain personally
22+ We use your data to provide and improve the Service. By using the
108− identifiable information that can be used to contact or identify you ("Personal
23+ Service, you agree to the collection and use of information in
109− Data"). Personally identifiable information may include, but is not limited to:
24+ accordance with this policy. Unless otherwise defined in this Privacy
25+ Policy, terms used in this Privacy Policy have the same meanings as in
26+ our Terms and Conditions.
110 </p>
27 </p>
28+ <p>Definitions</p>
29+ <p>Service</p>
111 <p>
30 <p>
112− First name and last name
31+ Service means the website and the Urbit mobile application operated by
32+ Tlon Corporation
113 </p>
33 </p>
34+ <p>Personal Data</p>
114 <p>
35 <p>
115− Cookies and Usage Data
36+ Personal Data means data about a living individual who can be
37+ identified from those data (or from those and other information either
38+ in our possession or likely to come into our possession).
116 </p>
39 </p>
40+ <p>Usage Data</p>
117 <p>
41 <p>
118− Usage Data
42+ Usage Data is data collected automatically either generated by the use
43+ of the Service or from the Service infrastructure itself (for example,
44+ the duration of a page visit).
119 </p>
45 </p>
46+ <p>Cookies</p>
120 <p>
47 <p>
121− We may also collect information that your browser sends whenever you visit our
48+ Cookies are small pieces of data stored on your device (computer or
122− Service or when you access the Service by or through a mobile device ("Usage
49+ mobile device).
123− Data").
124 </p>
50 </p>
51+ <p>Data Controller</p>
125 <p>
52 <p>
126− This Usage Data may include information such as your computer's Internet
53+ Data Controller means the natural or legal person who (either alone or
127− Protocol address (e.g. IP address), browser type, browser version, the pages of
54+ jointly or in common with other persons) determines the purposes for
128− our Service that you visit, the time and date of your visit, the time spent on
55+ which and the manner in which any personal information are, or are to
129− those pages, unique device identifiers and other diagnostic data.
56+ be, processed.
130 </p>
57 </p>
131 <p>
58 <p>
132− When you access the Service by or through a mobile device, this Usage Data may
59+ For the purpose of this Privacy Policy, we are a Data Controller of
133− include information such as the type of mobile device you use, your mobile
60+ your Personal Data.
134− device unique ID, the IP address of your mobile device, your mobile operating
135− system, the type of mobile Internet browser you use, unique device identifiers
136− and other diagnostic data.
137 </p>
61 </p>
62+ <p>Data Processors (or Service Providers)</p>
138 <p>
63 <p>
139− Tracking & Cookies Data
64+ Data Processor (or Service Provider) means any natural or legal person
65+ who processes the data on behalf of the Data Controller.
140 </p>
66 </p>
141 <p>
67 <p>
142− We use cookies and similar tracking technologies to track the activity on our
68+ We may use the services of various Service Providers in order to
143− Service and hold certain information.
69+ process your data more effectively.
144 </p>
70 </p>
71+ <p>Data Subject (or User)</p>
145 <p>
72 <p>
146− Cookies are files with small amount of data which may include an anonymous
73+ Data Subject is any living individual who is using our Service and is
147− unique identifier. Cookies are sent to your browser from a website and stored
74+ the subject of Personal Data.
148− on your device. Tracking technologies also used are beacons, tags, and scripts
149− to collect and track information and to improve and analyze our Service.
150 </p>
75 </p>
76+ <p>Information Collection and Use</p>
151 <p>
77 <p>
152− You can instruct your browser to refuse all cookies or to indicate when a
78+ We collect several different types of information for various purposes
153− cookie is being sent. However, if you do not accept cookies, you may not be
79+ to provide and improve our Service to you.
154− able to use some portions of our Service.
155 </p>
80 </p>
81+ <p>Types of Data Collected</p>
82+ <p>Personal Data</p>
156 <p>
83 <p>
157− Examples of Cookies we use:
84+ While using our Service, we may ask you to provide us with certain
85+ personally identifiable information that can be used to contact or
86+ identify you ("Personal Data"). Personally identifiable information
87+ may include, but is not limited to:
158 </p>
88 </p>
89+ <p>First name and last name</p>
90+ <p>Cookies and Usage Data</p>
91+ <p>Usage Data</p>
159 <p>
92 <p>
160− Session Cookies. We use Session Cookies to operate our Service.
93+ We may also collect information that your browser sends whenever you
94+ visit our Service or when you access the Service by or through a
95+ mobile device ("Usage Data").
161 </p>
96 </p>
162 <p>
97 <p>
163− Preference Cookies. We use Preference Cookies to remember your preferences and
98+ This Usage Data may include information such as your computer's
164− various settings.
99+ Internet Protocol address (e.g. IP address), browser type, browser
100+ version, the pages of our Service that you visit, the time and date of
101+ your visit, the time spent on those pages, unique device identifiers
102+ and other diagnostic data.
165 </p>
103 </p>
166 <p>
104 <p>
167− Security Cookies. We use Security Cookies for security purposes.
105+ When you access the Service by or through a mobile device, this Usage
106+ Data may include information such as the type of mobile device you
107+ use, your mobile device unique ID, the IP address of your mobile
108+ device, your mobile operating system, the type of mobile Internet
109+ browser you use, unique device identifiers and other diagnostic data.
168 </p>
110 </p>
111+ <p>Tracking & Cookies Data</p>
169 <p>
112 <p>
170− Use of Data
113+ We use cookies and similar tracking technologies to track the activity
114+ on our Service and hold certain information.
171 </p>
115 </p>
172 <p>
116 <p>
173− Tlon Corporation uses the collected data for various purposes:
117+ Cookies are files with small amount of data which may include an
118+ anonymous unique identifier. Cookies are sent to your browser from a
119+ website and stored on your device. Tracking technologies also used are
120+ beacons, tags, and scripts to collect and track information and to
121+ improve and analyze our Service.
174 </p>
122 </p>
175 <p>
123 <p>
176− To provide and maintain our Service
124+ You can instruct your browser to refuse all cookies or to indicate
125+ when a cookie is being sent. However, if you do not accept cookies,
126+ you may not be able to use some portions of our Service.
177 </p>
127 </p>
128+ <p>Examples of Cookies we use:</p>
129+ <p>Session Cookies. We use Session Cookies to operate our Service.</p>
178 <p>
130 <p>
179− To notify you about changes to our Service
131+ Preference Cookies. We use Preference Cookies to remember your
132+ preferences and various settings.
180 </p>
133 </p>
134+ <p>Security Cookies. We use Security Cookies for security purposes.</p>
135+ <p>Use of Data</p>
136+ <p>Tlon Corporation uses the collected data for various purposes:</p>
137+ <p>To provide and maintain our Service</p>
138+ <p>To notify you about changes to our Service</p>
181 <p>
139 <p>
182− To allow you to participate in interactive features of our Service when you
140+ To allow you to participate in interactive features of our Service
183− choose to do so
141+ when you choose to do so
184 </p>
142 </p>
143+ <p>To provide customer support</p>
185 <p>
144 <p>
186− To provide customer support
145+ To gather analysis or valuable information so that we can improve our
146+ Service
187 </p>
147 </p>
148+ <p>To monitor the usage of our Service</p>
149+ <p>To detect, prevent and address technical issues</p>
188 <p>
150 <p>
189− To gather analysis or valuable information so that we can improve our Service
151+ Legal Basis for Processing Personal Data Under General Data Protection
152+ Regulation (GDPR)
190 </p>
153 </p>
191 <p>
154 <p>
192− To monitor the usage of our Service
155+ If you are from the European Economic Area (EEA), Tlon Corporation
156+ legal basis for collecting and using the personal information
157+ described in this Privacy Policy depends on the Personal Data we
158+ collect and the specific context in which we collect it.
193 </p>
159 </p>
160+ <p>Tlon Corporation may process your Personal Data because:</p>
161+ <p>We need to perform a contract with you</p>
162+ <p>You have given us permission to do so</p>
194 <p>
163 <p>
195− To detect, prevent and address technical issues
164+ The processing is in our legitimate interests and it's not overridden
165+ by your rights
196 </p>
166 </p>
167+ <p>To comply with the law</p>
168+ <p>Retention of Data</p>
197 <p>
169 <p>
198− Legal Basis for Processing Personal Data Under General Data Protection
170+ Tlon Corporation will retain your Personal Data only for as long as is
199− Regulation (GDPR)
171+ necessary for the purposes set out in this Privacy Policy. We will
172+ retain and use your Personal Data to the extent necessary to comply
173+ with our legal obligations (for example, if we are required to retain
174+ your data to comply with applicable laws), resolve disputes, and
175+ enforce our legal agreements and policies.
200 </p>
176 </p>
201 <p>
177 <p>
202− If you are from the European Economic Area (EEA), Tlon Corporation legal basis
178+ Tlon Corporation will also retain Usage Data for internal analysis
203− for collecting and using the personal information described in this Privacy
179+ purposes. Usage Data is generally retained for a shorter period of
204− Policy depends on the Personal Data we collect and the specific context in
180+ time, except when this data is used to strengthen the security or to
205− which we collect it.
181+ improve the functionality of our Service, or we are legally obligated
182+ to retain this data for longer time periods.
206 </p>
183 </p>
184+ <p>Transfer of Data</p>
207 <p>
185 <p>
208− Tlon Corporation may process your Personal Data because:
186+ Your information, including Personal Data, may be transferred to — and
187+ maintained on — computers located outside of your state, province,
188+ country or other governmental jurisdiction where the data protection
189+ laws may differ than those from your jurisdiction.
209 </p>
190 </p>
210 <p>
191 <p>
211− We need to perform a contract with you
192+ If you are located outside United States and choose to provide
193+ information to us, please note that we transfer the data, including
194+ Personal Data, to United States and process it there.
212 </p>
195 </p>
213 <p>
196 <p>
214− You have given us permission to do so
197+ Your consent to this Privacy Policy followed by your submission of
198+ such information represents your agreement to that transfer.
215 </p>
199 </p>
216 <p>
200 <p>
217− The processing is in our legitimate interests and it's not overridden by your
201+ Tlon Corporation will take all steps reasonably necessary to ensure
218− rights
202+ that your data is treated securely and in accordance with this Privacy
203+ Policy and no transfer of your Personal Data will take place to an
204+ organization or a country unless there are adequate controls in place
205+ including the security of your data and other personal information.
219 </p>
206 </p>
207+ <p>Disclosure of Data</p>
208+ <p>Legal Requirements</p>
220 <p>
209 <p>
221− To comply with the law
210+ Tlon Corporation may disclose your Personal Data in the good faith
211+ belief that such action is necessary to:
222 </p>
212 </p>
213+ <p>To comply with a legal obligation</p>
214+ <p>To protect and defend the rights or property of Tlon Corporation</p>
223 <p>
215 <p>
224− Retention of Data
216+ To prevent or investigate possible wrongdoing in connection with the
217+ Service
225 </p>
218 </p>
226 <p>
219 <p>
227− Tlon Corporation will retain your Personal Data only for as long as is
220+ To protect the personal safety of users of the Service or the public
228− necessary for the purposes set out in this Privacy Policy. We will retain and
229− use your Personal Data to the extent necessary to comply with our legal
230− obligations (for example, if we are required to retain your data to comply with
231− applicable laws), resolve disputes, and enforce our legal agreements and
232− policies.
233 </p>
221 </p>
222+ <p>To protect against legal liability</p>
223+ <p>Security of Data</p>
234 <p>
224 <p>
235− Tlon Corporation will also retain Usage Data for internal analysis purposes.
225+ The security of your data is important to us, but remember that no
236− Usage Data is generally retained for a shorter period of time, except when this
226+ method of transmission over the Internet, or method of electronic
237− data is used to strengthen the security or to improve the functionality of our
227+ storage is 100% secure. While we strive to use commercially acceptable
238− Service, or we are legally obligated to retain this data for longer time
228+ means to protect your Personal Data, we cannot guarantee its absolute
239− periods.
229+ security.
240 </p>
230 </p>
231+ <p>"Do Not Track" Signals</p>
241 <p>
232 <p>
242− Transfer of Data
233+ We do not support Do Not Track ("DNT"). Do Not Track is a preference
234+ you can set in your web browser to inform websites that you do not
235+ want to be tracked.
243 </p>
236 </p>
244 <p>
237 <p>
245− Your information, including Personal Data, may be transferred to — and
238+ You can enable or disable Do Not Track by visiting the Preferences or
246− maintained on — computers located outside of your state, province, country or
239+ Settings page of your web browser.
247− other governmental jurisdiction where the data protection laws may differ than
248− those from your jurisdiction.
249 </p>
240 </p>
250 <p>
241 <p>
251− If you are located outside United States and choose to provide information to
242+ Your Data Protection Rights Under General Data Protection Regulation
252− us, please note that we transfer the data, including Personal Data, to United
243+ (GDPR)
253− States and process it there.
254 </p>
244 </p>
255 <p>
245 <p>
256− Your consent to this Privacy Policy followed by your submission of such
246+ If you are a resident of the European Economic Area (EEA), you have
257− information represents your agreement to that transfer.
247+ certain data protection rights. Tlon Corporation aims to take
248+ reasonable steps to allow you to correct, amend, delete, or limit the
249+ use of your Personal Data.
258 </p>
250 </p>
259 <p>
251 <p>
260− Tlon Corporation will take all steps reasonably necessary to ensure that your
252+ If you wish to be informed what Personal Data we hold about you and if
261− data is treated securely and in accordance with this Privacy Policy and no
253+ you want it to be removed from our systems, please contact us.
262− transfer of your Personal Data will take place to an organization or a country
263− unless there are adequate controls in place including the security of your data
264− and other personal information.
265 </p>
254 </p>
266 <p>
255 <p>
267− Disclosure of Data
256+ In certain circumstances, you have the following data protection
257+ rights:
268 </p>
258 </p>
269 <p>
259 <p>
270− Legal Requirements
260+ The right to access, update or to delete the information we have on
261+ you. Whenever made possible, you can access, update or request
262+ deletion of your Personal Data directly within your account settings
263+ section. If you are unable to perform these actions yourself, please
264+ contact us to assist you.
271 </p>
265 </p>
272 <p>
266 <p>
273− Tlon Corporation may disclose your Personal Data in the good faith belief that
267+ The right of rectification. You have the right to have your
274− such action is necessary to:
268+ information rectified if that information is inaccurate or incomplete.
275 </p>
269 </p>
276 <p>
270 <p>
277− To comply with a legal obligation
271+ The right to object. You have the right to object to our processing of
272+ your Personal Data.
278 </p>
273 </p>
279 <p>
274 <p>
280− To protect and defend the rights or property of Tlon Corporation
275+ The right of restriction. You have the right to request that we
276+ restrict the processing of your personal information.
281 </p>
277 </p>
282 <p>
278 <p>
283− To prevent or investigate possible wrongdoing in connection with the Service
279+ The right to data portability. You have the right to be provided with
280+ a copy of the information we have on you in a structured,
281+ machine-readable and commonly used format.
284 </p>
282 </p>
285 <p>
283 <p>
286− To protect the personal safety of users of the Service or the public
284+ The right to withdraw consent. You also have the right to withdraw
285+ your consent at any time where Tlon Corporation relied on your consent
286+ to process your personal information.
287 </p>
287 </p>
288 <p>
288 <p>
289− To protect against legal liability
289+ Please note that we may ask you to verify your identity before
290+ responding to such requests.
290 </p>
291 </p>
291 <p>
292 <p>
292− Security of Data
293+ You have the right to complain to a Data Protection Authority about
294+ our collection and use of your Personal Data. For more information,
295+ please contact your local data protection authority in the European
296+ Economic Area (EEA).
293 </p>
297 </p>
298+ <p>Service Providers</p>
294 <p>
299 <p>
295− The security of your data is important to us, but remember that no method of
300+ We may employ third party companies and individuals to facilitate our
296− transmission over the Internet, or method of electronic storage is 100% secure.
301+ Service ("Service Providers"), to provide the Service on our behalf,
297− While we strive to use commercially acceptable means to protect your Personal
302+ to perform Service-related services or to assist us in analyzing how
298− Data, we cannot guarantee its absolute security.
303+ our Service is used.
299 </p>
304 </p>
300 <p>
305 <p>
301− "Do Not Track" Signals
306+ These third parties have access to your Personal Data only to perform
307+ these tasks on our behalf and are obligated not to disclose or use it
308+ for any other purpose.
302 </p>
309 </p>
310+ <p>Links to Other Sites</p>
303 <p>
311 <p>
304− We do not support Do Not Track ("DNT"). Do Not Track is a preference you can
312+ Our Service may contain links to other sites that are not operated by
305− set in your web browser to inform websites that you do not want to be tracked.
313+ us. If you click on a third party link, you will be directed to that
314+ third party's site. We strongly advise you to review the Privacy
315+ Policy of every site you visit.
306 </p>
316 </p>
307 <p>
317 <p>
308− You can enable or disable Do Not Track by visiting the Preferences or Settings
318+ We have no control over and assume no responsibility for the content,
309− page of your web browser.
319+ privacy policies or practices of any third party sites or services.
310 </p>
320 </p>
321+ <p>Children's Privacy</p>
311 <p>
322 <p>
312− Your Data Protection Rights Under General Data Protection Regulation (GDPR)
323+ Our Service does not address anyone under the age of 18 ("Children").
313 </p>
324 </p>
314 <p>
325 <p>
315− If you are a resident of the European Economic Area (EEA), you have certain
326+ We do not knowingly collect personally identifiable information from
316− data protection rights. Tlon Corporation aims to take reasonable steps to allow
327+ anyone under the age of 18. If you are a parent or guardian and you
317− you to correct, amend, delete, or limit the use of your Personal Data.
328+ are aware that your child has provided us with Personal Data, please
329+ contact us. If we become aware that we have collected Personal Data
330+ from children without verification of parental consent, we take steps
331+ to remove that information from our servers.
318 </p>
332 </p>
333+ <p>Changes to This Privacy Policy</p>
319 <p>
334 <p>
320− If you wish to be informed what Personal Data we hold about you and if you want
335+ We may update our Privacy Policy from time to time. We will notify you
321− it to be removed from our systems, please contact us.
336+ of any changes by posting the new Privacy Policy on this page.
322 </p>
337 </p>
323 <p>
338 <p>
324− In certain circumstances, you have the following data protection rights:
339+ We will let you know via email and/or a prominent notice on our
340+ Service, prior to the change becoming effective and update the
341+ "effective date" at the top of this Privacy Policy.
325 </p>
342 </p>
326 <p>
343 <p>
327− The right to access, update or to delete the information we have on you.
344+ You are advised to review this Privacy Policy periodically for any
328− Whenever made possible, you can access, update or request deletion of your
345+ changes. Changes to this Privacy Policy are effective when they are
329− Personal Data directly within your account settings section. If you are unable
346+ posted on this page.
330− to perform these actions yourself, please contact us to assist you.
331 </p>
347 </p>
348+ <p>Contact Us</p>
332 <p>
349 <p>
333− The right of rectification. You have the right to have your information
350+ If you have any questions about this Privacy Policy, please contact
334− rectified if that information is inaccurate or incomplete.
351+ us:
335− </p>
336− <p>
337− The right to object. You have the right to object to our processing of your
338− Personal Data.
339− </p>
340− <p>
341− The right of restriction. You have the right to request that we restrict the
342− processing of your personal information.
343− </p>
344− <p>
345− The right to data portability. You have the right to be provided with a copy of
346− the information we have on you in a structured, machine-readable and commonly
347− used format.
348− </p>
349− <p>
350− The right to withdraw consent. You also have the right to withdraw your consent
351− at any time where Tlon Corporation relied on your consent to process your
352− personal information.
353− </p>
354− <p>
355− Please note that we may ask you to verify your identity before responding to
356− such requests.
357− </p>
358− <p>
359− You have the right to complain to a Data Protection Authority about our
360− collection and use of your Personal Data. For more information, please contact
361− your local data protection authority in the European Economic Area (EEA).
362− </p>
363− <p>
364− Service Providers
365− </p>
366− <p>
367− We may employ third party companies and individuals to facilitate our Service
368− ("Service Providers"), to provide the Service on our behalf, to perform
369− Service-related services or to assist us in analyzing how our Service is used.
370− </p>
371− <p>
372− These third parties have access to your Personal Data only to perform these
373− tasks on our behalf and are obligated not to disclose or use it for any other
374− purpose.
375− </p>
376− <p>
377− Links to Other Sites
378− </p>
379− <p>
380− Our Service may contain links to other sites that are not operated by us. If
381− you click on a third party link, you will be directed to that third party's
382− site. We strongly advise you to review the Privacy Policy of every site you
383− visit.
384− </p>
385− <p>
386− We have no control over and assume no responsibility for the content, privacy
387− policies or practices of any third party sites or services.
388− </p>
389− <p>
390− Children's Privacy
391− </p>
392− <p>
393− Our Service does not address anyone under the age of 18 ("Children").
394− </p>
395− <p>
396− We do not knowingly collect personally identifiable information from anyone
397− under the age of 18. If you are a parent or guardian and you are aware that
398− your child has provided us with Personal Data, please contact us. If we become
399− aware that we have collected Personal Data from children without verification
400− of parental consent, we take steps to remove that information from our servers.
401− </p>
402− <p>
403− Changes to This Privacy Policy
404− </p>
405− <p>
406− We may update our Privacy Policy from time to time. We will notify you of any
407− changes by posting the new Privacy Policy on this page.
408− </p>
409− <p>
410− We will let you know via email and/or a prominent notice on our Service, prior
411− to the change becoming effective and update the "effective date" at the top of
412− this Privacy Policy.
413− </p>
414− <p>
415− You are advised to review this Privacy Policy periodically for any changes.
416− Changes to this Privacy Policy are effective when they are posted on this page.
417− </p>
418− <p>
419− Contact Us
420− </p>
421− <p>
422− If you have any questions about this Privacy Policy, please contact us:
423− </p>
424− <p>
425− By email: support@urbit.org
426 </p>
352 </p>
353+ <p>By email: support@urbit.org</p>
427
354
428 <div>
355 <div>
429−
430 <Button
356 <Button
431 className={'btn btn-primary'}
357 className={'btn btn-primary'}
432 text={'Close'}
358 text={'Close'}
433− onClick={ () => setGlobalState({ 'route': lastRoute })}
359+ onClick={() => setGlobalState({ route: lastRoute })}
434 />
360 />
435−
436 </div>
361 </div>
437−
438 </div>
362 </div>
439− )
363+ );
440 }
364 }
441}
365}
442
366
443−export default PrivacyPolicy
367+export default PrivacyPolicy;
src/walletgen/views/Understand.js+37−14
@@ -1,6 +1,6 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import Button from '../components/Button'
3+import Button from '../components/Button';
4
4
5const NEXT_STEP_NUM = 4;
5const NEXT_STEP_NUM = 4;
6
6
@@ -8,36 +8,59 @@ class Understand extends React.Component {
8 render() {
8 render() {
9 const { setGlobalState } = this.props;
9 const { setGlobalState } = this.props;
10
10
11− return(
11+ return (
12 <div className={'col-md-6'}>
12 <div className={'col-md-6'}>
13− <h2 className={'mb-4'}>{"First, let's look at the HD Wallet in more detail."}</h2>
13+ <h2 className={'mb-4'}>
14+ {"First, let's look at the HD Wallet in more detail."}
15+ </h2>
14
16
15 <p>{`Urbit HD wallets contain the following important items.`}</p>
17 <p>{`Urbit HD wallets contain the following important items.`}</p>
16
18
17 <h3 className={'mt-4 mb-0'}>Master Ticket</h3>
19 <h3 className={'mt-4 mb-0'}>Master Ticket</h3>
18− <p>Think of your master ticket like a very high value password. The master ticket is the secret code from which all of your other keys are derived. Technically, your master ticket is seed entropy. You should never share it with anyone, and store it very securely. This ticket can derive all of your other keys: your ownership key and all of the related proxies. </p>
20+ <p>
21+ Think of your master ticket like a very high value password. The
22+ master ticket is the secret code from which all of your other keys are
23+ derived. Technically, your master ticket is seed entropy. You should
24+ never share it with anyone, and store it very securely. This ticket
25+ can derive all of your other keys: your ownership key and all of the
26+ related proxies.{' '}
27+ </p>
19 <h3 className={'mt-4 mb-0'}>Ownership Address</h3>
28 <h3 className={'mt-4 mb-0'}>Ownership Address</h3>
20− <p>An ownership address has all rights over the assets deeded to it. These rights are on-chain actions described and implemented in the Constitution, Urbit’s Ethereum smart contracts.</p>
29+ <p>
30+ An ownership address has all rights over the assets deeded to it.
31+ These rights are on-chain actions described and implemented in the
32+ Constitution, Urbit’s Ethereum smart contracts.
33+ </p>
21 <h3 className={'mt-4 mb-0'}>Proxies</h3>
34 <h3 className={'mt-4 mb-0'}>Proxies</h3>
22 <p>{`Proxy addresses allow you to execute non-ownership related actions like spawning child ships, voting, and setting networking keys without jeopardizing the keys you've designated with ownership rights. Setting proxy rights is optional, but it is recommended for on-chain actions you will execute more frequently.`}</p>
35 <p>{`Proxy addresses allow you to execute non-ownership related actions like spawning child ships, voting, and setting networking keys without jeopardizing the keys you've designated with ownership rights. Setting proxy rights is optional, but it is recommended for on-chain actions you will execute more frequently.`}</p>
23 <h3 className={'mt-4 mb-0 ml-8'}>Management Proxy</h3>
36 <h3 className={'mt-4 mb-0 ml-8'}>Management Proxy</h3>
24− <p className={'ml-8'}>Can configure or set Urbit networking keys and conduct sponsorship related operations.</p>
37+ <p className={'ml-8'}>
38+ Can configure or set Urbit networking keys and conduct sponsorship
39+ related operations.
40+ </p>
25 <h3 className={'mt-4 mb-0 ml-8'}>Voting Proxy</h3>
41 <h3 className={'mt-4 mb-0 ml-8'}>Voting Proxy</h3>
26− <p className={'ml-8'}>Galaxies only. Galaxies are the part of the galactic senate, and this means they can cast votes on new proposals including changes to the Constitution.</p>
42+ <p className={'ml-8'}>
43+ Galaxies only. Galaxies are the part of the galactic senate, and this
44+ means they can cast votes on new proposals including changes to the
45+ Constitution.
46+ </p>
27 <h3 className={'mt-4 mb-0 ml-8'}>Spawn Proxy</h3>
47 <h3 className={'mt-4 mb-0 ml-8'}>Spawn Proxy</h3>
28− <p className={'ml-8'}>For stars and galaxies only. Can create new child ships.</p>
48+ <p className={'ml-8'}>
49+ For stars and galaxies only. Can create new child ships.
50+ </p>
29
51
30 <div className={'btn-tray'}>
52 <div className={'btn-tray'}>
31 <Button
53 <Button
32 className={'btn btn-primary'}
54 className={'btn btn-primary'}
33− text={"Ok, got it →"}
55+ text={'Ok, got it →'}
34− onClick={ () => setGlobalState({ route: '/Custody', 'currentStep': NEXT_STEP_NUM })}
56+ onClick={() =>
57+ setGlobalState({ route: '/Custody', currentStep: NEXT_STEP_NUM })
58+ }
35 />
59 />
36 </div>
60 </div>
37−
38 </div>
61 </div>
39− )
62+ );
40 }
63 }
41}
64}
42
65
43−export default Understand
66+export default Understand;
src/walletgen/views/Upload.js+126−105
@@ -1,57 +1,62 @@
1−import React, { Component } from 'react'
1+import React, { Component } from 'react';
2
2
3−import Button from '../components/Button'
3+import Button from '../components/Button';
4−import UploadButton from '../components/UploadButton'
4+import UploadButton from '../components/UploadButton';
5
5
6−import { compose } from '../lib/lib'
6+import { compose } from '../lib/lib';
7−
8−import { PROFILE_STATES } from '../lib/constants'
9
7
8+import { PROFILE_STATES } from '../lib/constants';
10
9
11const NEXT_STEP_NUM = 3;
10const NEXT_STEP_NUM = 3;
12
11
13const validateBase64 = ({ data, pass, error }) => {
12const validateBase64 = ({ data, pass, error }) => {
14− if (pass !== true) return { data, pass, error }
13+ if (pass !== true) return { data, pass, error };
15
14
16 try {
15 try {
17 atob(data);
16 atob(data);
18− return { data: atob(data), pass: true, error: '' }
17+ return { data: atob(data), pass: true, error: '' };
19− } catch(e) {
18+ } catch (e) {
20− console.error(e)
19+ console.error(e);
21− return { data: data, pass: false, error: 'File is not valid base64.' }
20+ return { data: data, pass: false, error: 'File is not valid base64.' };
22 }
21 }
23−}
22+};
24
23
25const validateJSON = ({ data, pass, error }) => {
24const validateJSON = ({ data, pass, error }) => {
26− if (pass !== true) return { data, pass, error }
25+ if (pass !== true) return { data, pass, error };
27
26
28 try {
27 try {
29− JSON.parse(data)
28+ JSON.parse(data);
30− return { data: JSON.parse(data), pass: true, error: '' }
29+ return { data: JSON.parse(data), pass: true, error: '' };
31− } catch(e) {
30+ } catch (e) {
32− console.error(e)
31+ console.error(e);
33− return { data: data, pass: false, error: 'File does not decode into valid JSON.' }
32+ return {
33+ data: data,
34+ pass: false,
35+ error: 'File does not decode into valid JSON.',
36+ };
34 }
37 }
35−}
38+};
36
39
37const validateFile = ({ data, pass, error }) => {
40const validateFile = ({ data, pass, error }) => {
38 if (pass !== true) return { data, pass, error };
41 if (pass !== true) return { data, pass, error };
39
42
40 if (data.file.type !== 'text/plain') {
43 if (data.file.type !== 'text/plain') {
41− return { data: data, pass: false, error: 'File must be a .txt file' }
44+ return { data: data, pass: false, error: 'File must be a .txt file' };
42− };
45+ }
43
46
44 if (data.file.name.includes('urbit-ships') === false) {
47 if (data.file.name.includes('urbit-ships') === false) {
45− return { data: data, pass: false, error: 'This does not appear to be the correct file.' }
48+ return {
46− };
49+ data: data,
50+ pass: false,
51+ error: 'This does not appear to be the correct file.',
52+ };
53+ }
47
54
48 return { data: data.event.target.result, pass, error };
55 return { data: data.event.target.result, pass, error };
49−
56+};
50−}
51−
52
57
53const validateObject = ({ data, pass, error }) => {
58const validateObject = ({ data, pass, error }) => {
54− if (pass !== true) return { data, pass, error }
59+ if (pass !== true) return { data, pass, error };
55
60
56 const checkStringField = (obj, field) =>
61 const checkStringField = (obj, field) =>
57 field in obj && typeof obj[field] === 'string';
62 field in obj && typeof obj[field] === 'string';
@@ -60,20 +65,20 @@ const validateObject = ({ data, pass, error }) => {
60 field in obj || Array.isArray(obj[field]);
65 field in obj || Array.isArray(obj[field]);
61
66
62 const meetsRequirements = obj =>
67 const meetsRequirements = obj =>
63− checkStringField(obj,'idCode')
68+ checkStringField(obj, 'idCode') &&
64− && checkShipField(obj,'planets')
69+ checkShipField(obj, 'planets') &&
65− && checkShipField(obj,'stars')
70+ checkShipField(obj, 'stars') &&
66− && checkShipField(obj,'galaxies');
71+ checkShipField(obj, 'galaxies');
67−
68
72
69 const didPass = meetsRequirements(data) ? true : false;
73 const didPass = meetsRequirements(data) ? true : false;
70
74
71− const err = didPass === false
75+ const err =
72− ? 'File is missing data, or the data is of incorrect data type.'
76+ didPass === false
73− : '';
77+ ? 'File is missing data, or the data is of incorrect data type.'
78+ : '';
74
79
75− return { data: data, pass: didPass, error: err }
80+ return { data: data, pass: didPass, error: err };
76−}
81+};
77
82
78// const preview = ship => {
83// const preview = ship => {
79// const name = ob.patp(ship)
84// const name = ob.patp(ship)
@@ -97,12 +102,12 @@ const validateObject = ({ data, pass, error }) => {
97// </div>
102// </div>
98// </div>
103// </div>
99// </div>
104// </div>
100− // <div className={'flex h-16 w-60 bg-white black items-center'} key={ ship }>
105+// <div className={'flex h-16 w-60 bg-white black items-center'} key={ ship }>
101− // { sig }
106+// { sig }
102− // <div className={'pl-4 text-mono text-500'}>
107+// <div className={'pl-4 text-mono text-500'}>
103− // { name }
108+// { name }
104− // </div>
109+// </div>
105− // </div>
110+// </div>
106// )
111// )
107// }
112// }
108
113
@@ -121,44 +126,42 @@ const validateObject = ({ data, pass, error }) => {
121
126
122class Upload extends Component {
127class Upload extends Component {
123 constructor(props) {
128 constructor(props) {
124− super(props)
129+ super(props);
125− this.handleTXTUpload = this.handleTXTUpload.bind(this)
130+ this.handleTXTUpload = this.handleTXTUpload.bind(this);
126 }
131 }
127
132
133+ handleTXTUpload = event => {
134+ const { setGlobalState } = this.props;
135+ const file = event.files.item(0);
136+ const reader = new FileReader();
128
137
129− handleTXTUpload = (event) => {
138+ reader.onload = event => {
130− const { setGlobalState } = this.props
131− const file = event.files.item(0)
132− const reader = new FileReader()
133−
134− reader.onload = (event) => {
135−
136 const validated = compose(
139 const validated = compose(
137 validateObject,
140 validateObject,
138 validateJSON,
141 validateJSON,
139 validateBase64,
142 validateBase64,
140 validateFile
143 validateFile
141− )({ data: { event: event, file: file }, pass: true, error: '' })
144+ )({ data: { event: event, file: file }, pass: true, error: '' });
142
145
143 if (validated.pass === true) {
146 if (validated.pass === true) {
144− const { galaxies, stars, planets } = validated.data
147+ const { galaxies, stars, planets } = validated.data;
145 setGlobalState({
148 setGlobalState({
146 'profile.error': validated.error,
149 'profile.error': validated.error,
147 'profile.state': PROFILE_STATES.UPLOAD_SUCCESS,
150 'profile.state': PROFILE_STATES.UPLOAD_SUCCESS,
148 'profile.value': validated.data,
151 'profile.value': validated.data,
149− 'profile.shipCount': galaxies.length + stars.length + planets.length
152+ 'profile.shipCount': galaxies.length + stars.length + planets.length,
150− })
153+ });
151 } else {
154 } else {
152 setGlobalState({
155 setGlobalState({
153 'profile.error': validated.error,
156 'profile.error': validated.error,
154 'profile.state': PROFILE_STATES.UPLOAD_ERROR,
157 'profile.state': PROFILE_STATES.UPLOAD_ERROR,
155 'profile.value': null,
158 'profile.value': null,
156− })
159+ });
157 }
160 }
158− }
161+ };
159
162
160− reader.readAsText(file)
163+ reader.readAsText(file);
161− }
164+ };
162
165
163 render() {
166 render() {
164 const { setGlobalState } = this.props;
167 const { setGlobalState } = this.props;
@@ -167,72 +170,90 @@ class Upload extends Component {
167
170
168 const success =
171 const success =
169 props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS ||
172 props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS ||
170− props['profile.state'] === PROFILE_STATES.INPUT_SUCCESS
173+ props['profile.state'] === PROFILE_STATES.INPUT_SUCCESS;
171
174
172− let uploadButtonClass = props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
175+ let uploadButtonClass =
173− ? 'btn shape-orange'
176+ props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
174− : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
177+ ? 'btn shape-orange'
178+ : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
175 ? 'btn btn-success'
179 ? 'btn btn-success'
176− : 'btn btn-primary'
180+ : 'btn btn-primary';
177
181
178− let uploadButtonText = props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
182+ let uploadButtonText =
179− ? 'Choose file again'
183+ props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
180− : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
184+ ? 'Choose file again'
185+ : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
181 ? 'urbit-ships.txt'
186 ? 'urbit-ships.txt'
182− : 'Choose file'
187+ : 'Choose file';
183
188
184− return(
189+ return (
185 <div className={'col-md-6'}>
190 <div className={'col-md-6'}>
186− <h2>{'Upload your '}<code>{'urbit-ships.txt'}</code></h2>
191+ <h2>
192+ {'Upload your '}
193+ <code>{'urbit-ships.txt'}</code>
194+ </h2>
187
195
188− <p>{'This file contains your email and tells us what Urbit address space you own. Don’t have it? Return to Registration to download it.'}</p>
196+ <p>
197+ {
198+ 'This file contains your email and tells us what Urbit address space you own. Don’t have it? Return to Registration to download it.'
199+ }
200+ </p>
189
201
190 <UploadButton
202 <UploadButton
191− className={ `${uploadButtonClass} table mt-4` }
203+ className={`${uploadButtonClass} table mt-4`}
192− onChange={ this.handleTXTUpload }
204+ onChange={this.handleTXTUpload}
193− accept={'.txt'}
205+ accept={'.txt'}>
194− >
195 {uploadButtonText}
206 {uploadButtonText}
196 </UploadButton>
207 </UploadButton>
197
208
198− { props['profil.error'] !== ''
209+ {props['profil.error'] !== '' ? (
199− ? <p className={'orange'}>{props['profile.error']}</p>
210+ <p className={'orange'}>{props['profile.error']}</p>
200− : <div />
211+ ) : (
212+ <div />
213+ )}
214+ {
215+ // { success
216+ // ? <ShipList ships={props['profile.value'].galaxies} title={'Galaxies'} />
217+ // : <div />
218+ // }
219+ //
220+ // { success
221+ // ? <ShipList ships={props['profile.value'].stars} title={'Stars'} />
222+ // : <div />
223+ // }
224+ //
225+ // { success
226+ // ? <ShipList ships={props['profile.value'].planet} title={'Planets'} />
227+ // : <div />
228+ // }
201 }
229 }
202−{
203− // { success
204− // ? <ShipList ships={props['profile.value'].galaxies} title={'Galaxies'} />
205− // : <div />
206− // }
207− //
208− // { success
209− // ? <ShipList ships={props['profile.value'].stars} title={'Stars'} />
210− // : <div />
211− // }
212− //
213− // { success
214− // ? <ShipList ships={props['profile.value'].planet} title={'Planets'} />
215− // : <div />
216− // }
217−}
218
230
219 <div className={'btn-tray'}>
231 <div className={'btn-tray'}>
220− { success
232+ {success ? (
221− ? <div>
233+ <div>
222 {
234 {
223 // <p>Missing ships? Email <a href={'mailto:registration@urbit.org'}>{'registration@urbit.org'}</a></p>
235 // <p>Missing ships? Email <a href={'mailto:registration@urbit.org'}>{'registration@urbit.org'}</a></p>
224 }
236 }
225 <Button
237 <Button
226− className={ success ? 'btn btn-primary mt-8' : 'btn shape-gray-10'}
238+ className={
227− disabled={ !success }
239+ success ? 'btn btn-primary mt-8' : 'btn shape-gray-10'
240+ }
241+ disabled={!success}
228 text={"I'm ready to begin →"}
242 text={"I'm ready to begin →"}
229− onClick={ () => setGlobalState({ route: '/Understand', 'currentStep': NEXT_STEP_NUM })} />
243+ onClick={() =>
244+ setGlobalState({
245+ route: '/Understand',
246+ currentStep: NEXT_STEP_NUM,
247+ })
248+ }
249+ />
230 </div>
250 </div>
231− : <div />
251+ ) : (
232− }
252+ <div />
233− </ div>
253+ )}
254+ </div>
234 </div>
255 </div>
235− )
256+ );
236 }
257 }
237}
258}
238
259
@@ -249,4 +270,4 @@ class Upload extends Component {
249// }
270// }
250// }
271// }
251
272
252−export default Upload
273+export default Upload;
src/walletgen/views/Welcome.js+23−10
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
2
2
3−import Button from '../components/Button'
3+import Button from '../components/Button';
4
4
5const NEXT_STEP_NUM = 1;
5const NEXT_STEP_NUM = 1;
6
6
7class Welcome extends React.Component {
7class Welcome extends React.Component {
8 render() {
8 render() {
9− let { setGlobalState } = this.props
9+ let { setGlobalState } = this.props;
10
10
11− return(
11+ return (
12 <div>
12 <div>
13 <div className={'col-md-7'}>
13 <div className={'col-md-7'}>
14 <h2>{`Welcome`}</h2>
14 <h2>{`Welcome`}</h2>
@@ -17,21 +17,34 @@ class Welcome extends React.Component {
17 {`If you came here from Registration and you’re ready to make a wallet for your ships. You’ve come to the right place.`}
17 {`If you came here from Registration and you’re ready to make a wallet for your ships. You’ve come to the right place.`}
18 </p>
18 </p>
19
19
20− <p>To start, you need to tell us about the ships you own by uploading the <code>urbit-ships.txt</code> you should have downloaded from the Registration website. If you didn’t get this file, please return to Registration and download it.</p>
20+ <p>
21− <p>Reminder: Run this software offline and disable any browser extensions. If you highly value your ships, please use a new, airgapped computer.</p>
21+ To start, you need to tell us about the ships you own by uploading
22+ the <code>urbit-ships.txt</code> you should have downloaded from the
23+ Registration website. If you didn’t get this file, please return to
24+ Registration and download it.
25+ </p>
26+ <p>
27+ Reminder: Run this software offline and disable any browser
28+ extensions. If you highly value your ships, please use a new,
29+ airgapped computer.
30+ </p>
22
31
23 <div className={'btn-tray'}>
32 <div className={'btn-tray'}>
24− <p><strong>{`When you’re ready to begin, start by accepting our End User License Agreement.`}</strong></p>
33+ <p>
34+ <strong>{`When you’re ready to begin, start by accepting our End User License Agreement.`}</strong>
35+ </p>
25 <Button
36 <Button
26 className={'btn btn-primary mt-8'}
37 className={'btn btn-primary mt-8'}
27 text={'View →'}
38 text={'View →'}
28− onClick={ () => setGlobalState({ 'route': '/Eula', 'currentStep': NEXT_STEP_NUM })}
39+ onClick={() =>
40+ setGlobalState({ route: '/Eula', currentStep: NEXT_STEP_NUM })
41+ }
29 />
42 />
30 </div>
43 </div>
31 </div>
44 </div>
32 </div>
45 </div>
33− )
46+ );
34 }
47 }
35}
48}
36
49
37−export default Welcome
50+export default Welcome;