on master help sign in
feat: implement very simple router with contextual state
1a0f264Matt Condon (shrugs)
package-lock.json+11−103
@@ -7702,28 +7702,6 @@
7702 "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
7702 "resolved": "https://registry.npmjs.org/hex-color-regex/-/hex-color-regex-1.1.0.tgz",
7703 "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
7703 "integrity": "sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ=="
7704 },
7704 },
7705− "history": {
7706− "version": "4.7.2",
7707− "resolved": "https://registry.npmjs.org/history/-/history-4.7.2.tgz",
7708− "integrity": "sha512-1zkBRWW6XweO0NBcjiphtVJVsIQ+SXF29z9DVkceeaSLVMFXHool+fdCZD4spDCfZJCILPILc3bm7Bc+HRi0nA==",
7709− "requires": {
7710− "invariant": "^2.2.1",
7711− "loose-envify": "^1.2.0",
7712− "resolve-pathname": "^2.2.0",
7713− "value-equal": "^0.4.0",
7714− "warning": "^3.0.0"
7715− },
7716− "dependencies": {
7717− "warning": {
7718− "version": "3.0.0",
7719− "resolved": "https://registry.npmjs.org/warning/-/warning-3.0.0.tgz",
7720− "integrity": "sha1-MuU3fLVy3kqwR1O9+IIcAe1gW3w=",
7721− "requires": {
7722− "loose-envify": "^1.0.0"
7723− }
7724− }
7725− }
7726− },
7727 "hmac-drbg": {
7705 "hmac-drbg": {
7728 "version": "1.0.1",
7706 "version": "1.0.1",
7729 "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
7707 "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",
@@ -7739,11 +7717,6 @@
7739 "resolved": "http://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
7717 "resolved": "http://registry.npmjs.org/hoek/-/hoek-4.2.1.tgz",
7740 "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
7718 "integrity": "sha512-QLg82fGkfnJ/4iy1xZ81/9SIJiq1NGFUMGs6ParyjBZr6jW2Ufj/snDqTHixNlHdPNwN2RLVD0Pi3igeK9+JfA=="
7741 },
7719 },
7742− "hoist-non-react-statics": {
7743− "version": "2.5.5",
7744− "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz",
7745− "integrity": "sha512-rqcy4pJo55FTTLWt+bU8ukscqHeE/e9KWvsOW2b/a3afxQZhwkQdT1rPPCJ0rYXdj4vNcasY8zHTH+jF/qStxw=="
7746− },
7747 "home-or-tmp": {
7720 "home-or-tmp": {
7748 "version": "2.0.0",
7721 "version": "2.0.0",
7749 "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
7722 "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz",
@@ -8515,11 +8488,6 @@
8515 "resolved": "https://registry.npmjs.org/immer/-/immer-1.7.2.tgz",
8488 "resolved": "https://registry.npmjs.org/immer/-/immer-1.7.2.tgz",
8516 "integrity": "sha512-4Urocwu9+XLDJw4Tc6ZCg7APVjjLInCFvO4TwGsAYV5zT6YYSor14dsZR0+0tHlDIN92cFUOq+i7fC00G5vTxA=="
8489 "integrity": "sha512-4Urocwu9+XLDJw4Tc6ZCg7APVjjLInCFvO4TwGsAYV5zT6YYSor14dsZR0+0tHlDIN92cFUOq+i7fC00G5vTxA=="
8517 },
8490 },
8518− "immutable": {
8519− "version": "4.0.0-rc.12",
8520− "resolved": "https://registry.npmjs.org/immutable/-/immutable-4.0.0-rc.12.tgz",
8521− "integrity": "sha512-0M2XxkZLx/mi3t8NVwIm1g8nHoEmM9p9UBl/G9k4+hm0kBgOVdMV/B3CY5dQ8qG8qc80NN4gDV4HQv6FTJ5q7A=="
8522− },
8523 "import-cwd": {
8491 "import-cwd": {
8524 "version": "2.1.0",
8492 "version": "2.1.0",
8525 "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
8493 "resolved": "https://registry.npmjs.org/import-cwd/-/import-cwd-2.1.0.tgz",
@@ -15979,14 +15947,14 @@
15979 }
15947 }
15980 },
15948 },
15981 "react": {
15949 "react": {
15982− "version": "16.6.3",
15950+ "version": "16.8.6",
15983− "resolved": "https://registry.npmjs.org/react/-/react-16.6.3.tgz",
15951+ "resolved": "https://registry.npmjs.org/react/-/react-16.8.6.tgz",
15984− "integrity": "sha512-zCvmH2vbEolgKxtqXL2wmGCUxUyNheYn/C+PD1YAjfxHC54+MhdruyhO7QieQrYsYeTxrn93PM2y0jRH1zEExw==",
15952+ "integrity": "sha512-pC0uMkhLaHm11ZSJULfOBqV4tIZkx87ZLvbbQYunNixAAvjnC+snJCg0XQXn9VIsttVsbZP/H/ewzgsd5fxKXw==",
15985 "requires": {
15953 "requires": {
15986 "loose-envify": "^1.1.0",
15954 "loose-envify": "^1.1.0",
15987 "object-assign": "^4.1.1",
15955 "object-assign": "^4.1.1",
15988 "prop-types": "^15.6.2",
15956 "prop-types": "^15.6.2",
15989− "scheduler": "^0.11.2"
15957+ "scheduler": "^0.13.6"
15990 }
15958 }
15991 },
15959 },
15992 "react-app-polyfill": {
15960 "react-app-polyfill": {
@@ -16188,14 +16156,14 @@
16188 }
16156 }
16189 },
16157 },
16190 "react-dom": {
16158 "react-dom": {
16191− "version": "16.6.3",
16159+ "version": "16.8.6",
16192− "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.6.3.tgz",
16160+ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-16.8.6.tgz",
16193− "integrity": "sha512-8ugJWRCWLGXy+7PmNh8WJz3g1TaTUt1XyoIcFN+x0Zbkoz+KKdUyx1AQLYJdbFXjuF41Nmjn5+j//rxvhFjgSQ==",
16161+ "integrity": "sha512-1nL7PIq9LTL3fthPqwkvr2zY7phIPjYrT0jp4HjyEQrEROnw4dG41VVwi/wfoCneoleqrNX7iAD+pXebJZwrwA==",
16194 "requires": {
16162 "requires": {
16195 "loose-envify": "^1.1.0",
16163 "loose-envify": "^1.1.0",
16196 "object-assign": "^4.1.1",
16164 "object-assign": "^4.1.1",
16197 "prop-types": "^15.6.2",
16165 "prop-types": "^15.6.2",
16198− "scheduler": "^0.11.2"
16166+ "scheduler": "^0.13.6"
16199 }
16167 }
16200 },
16168 },
16201 "react-error-overlay": {
16169 "react-error-overlay": {
@@ -16203,48 +16171,6 @@
16203 "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.1.tgz",
16171 "resolved": "https://registry.npmjs.org/react-error-overlay/-/react-error-overlay-5.1.1.tgz",
16204 "integrity": "sha512-gJcOyak5ri9mzhpFObSAkCqNNBCVZE8LaR/mX37agULgSL0dobgX11NVSIetvNzhHMkr6CIqV0xiutvvcjBtkw=="
16172 "integrity": "sha512-gJcOyak5ri9mzhpFObSAkCqNNBCVZE8LaR/mX37agULgSL0dobgX11NVSIetvNzhHMkr6CIqV0xiutvvcjBtkw=="
16205 },
16173 },
16206− "react-router": {
16207− "version": "4.3.1",
16208− "resolved": "https://registry.npmjs.org/react-router/-/react-router-4.3.1.tgz",
16209− "integrity": "sha512-yrvL8AogDh2X42Dt9iknk4wF4V8bWREPirFfS9gLU1huk6qK41sg7Z/1S81jjTrGHxa3B8R3J6xIkDAA6CVarg==",
16210− "requires": {
16211− "history": "^4.7.2",
16212− "hoist-non-react-statics": "^2.5.0",
16213− "invariant": "^2.2.4",
16214− "loose-envify": "^1.3.1",
16215− "path-to-regexp": "^1.7.0",
16216− "prop-types": "^15.6.1",
16217− "warning": "^4.0.1"
16218− },
16219− "dependencies": {
16220− "isarray": {
16221− "version": "0.0.1",
16222− "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz",
16223− "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8="
16224− },
16225− "path-to-regexp": {
16226− "version": "1.7.0",
16227− "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.7.0.tgz",
16228− "integrity": "sha1-Wf3g9DW62suhA6hOnTvGTpa5k30=",
16229− "requires": {
16230− "isarray": "0.0.1"
16231− }
16232− }
16233− }
16234− },
16235− "react-router-dom": {
16236− "version": "4.3.1",
16237− "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-4.3.1.tgz",
16238− "integrity": "sha512-c/MlywfxDdCp7EnB7YfPMOfMD3tOtIjrQlj/CKfNMBxdmpJP8xcz5P/UAFn3JbnQCNUxsHyVVqllF9LhgVyFCA==",
16239− "requires": {
16240− "history": "^4.7.2",
16241− "invariant": "^2.2.4",
16242− "loose-envify": "^1.3.1",
16243− "prop-types": "^15.6.1",
16244− "react-router": "^4.3.1",
16245− "warning": "^4.0.1"
16246− }
16247− },
16248 "react-scripts": {
16174 "react-scripts": {
16249 "version": "2.1.2",
16175 "version": "2.1.2",
16250 "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.2.tgz",
16176 "resolved": "https://registry.npmjs.org/react-scripts/-/react-scripts-2.1.2.tgz",
@@ -16951,11 +16877,6 @@
16951 "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
16877 "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-3.0.0.tgz",
16952 "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
16878 "integrity": "sha1-six699nWiBvItuZTM17rywoYh0g="
16953 },
16879 },
16954− "resolve-pathname": {
16955− "version": "2.2.0",
16956− "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-2.2.0.tgz",
16957− "integrity": "sha512-bAFz9ld18RzJfddgrO2e/0S2O81710++chRMUxHjXOYKF6jTAMrUNZrEZ1PvV0zlhfjidm08iRPdTLPno1FuRg=="
16958− },
16959 "resolve-url": {
16880 "resolve-url": {
16960 "version": "0.2.1",
16881 "version": "0.2.1",
16961 "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
16882 "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz",
@@ -17432,9 +17353,9 @@
17432 }
17353 }
17433 },
17354 },
17434 "scheduler": {
17355 "scheduler": {
17435− "version": "0.11.3",
17356+ "version": "0.13.6",
17436− "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.11.3.tgz",
17357+ "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.13.6.tgz",
17437− "integrity": "sha512-i9X9VRRVZDd3xZw10NY5Z2cVMbdYg6gqFecfj79USv1CFN+YrJ3gIPRKf1qlY+Sxly4djoKdfx1T+m9dnRB8kQ==",
17358+ "integrity": "sha512-IWnObHt413ucAYKsD9J1QShUKkbKLQQHdxRyw73sw4FN26iWr3DY/H34xGPe4nmL1DwXyWmSWmMrA9TfQbE/XQ==",
17438 "requires": {
17359 "requires": {
17439 "loose-envify": "^1.1.0",
17360 "loose-envify": "^1.1.0",
17440 "object-assign": "^4.1.1"
17361 "object-assign": "^4.1.1"
@@ -19517,11 +19438,6 @@
19517 "spdx-expression-parse": "^3.0.0"
19438 "spdx-expression-parse": "^3.0.0"
19518 }
19439 }
19519 },
19440 },
19520− "value-equal": {
19521− "version": "0.4.0",
19522− "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-0.4.0.tgz",
19523− "integrity": "sha512-x+cYdNnaA3CxvMaTX0INdTCN8m8aF2uY9BvEqmxuYp8bL09cs/kWVQPVGcA35fMktdOsP69IgU7wFj/61dJHEw=="
19524− },
19525 "vary": {
19441 "vary": {
19526 "version": "1.1.2",
19442 "version": "1.1.2",
19527 "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
19443 "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
@@ -19576,14 +19492,6 @@
19576 "makeerror": "1.0.x"
19492 "makeerror": "1.0.x"
19577 }
19493 }
19578 },
19494 },
19579− "warning": {
19580− "version": "4.0.3",
19581− "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz",
19582− "integrity": "sha512-rpJyN222KWIvHJ/F53XSZv0Zl/accqHR8et1kpaMTD/fLCRxtV8iX8czMzY7sVZupTI3zcUTg8eycS2kNF9l6w==",
19583− "requires": {
19584− "loose-envify": "^1.0.0"
19585− }
19586− },
19587 "watch": {
19495 "watch": {
19588 "version": "0.18.0",
19496 "version": "0.18.0",
19589 "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
19497 "resolved": "https://registry.npmjs.org/watch/-/watch-0.18.0.tgz",
package.json+2−4
@@ -29,16 +29,14 @@
29 "ethereumjs-tx": "^1.3.7",
29 "ethereumjs-tx": "^1.3.7",
30 "file-saver": "^2.0.0",
30 "file-saver": "^2.0.0",
31 "folktale": "^2.3.1",
31 "folktale": "^2.3.1",
32− "immutable": "^4.0.0-rc.12",
33 "jszip": "^3.1.5",
32 "jszip": "^3.1.5",
34 "keccak": "^1.4.0",
33 "keccak": "^1.4.0",
35 "keythereum": "^1.0.4",
34 "keythereum": "^1.0.4",
36 "lodash": "^4.17.11",
35 "lodash": "^4.17.11",
37 "more-entropy": "^0.0.7",
36 "more-entropy": "^0.0.7",
38 "prop-types": "^15.6.2",
37 "prop-types": "^15.6.2",
39− "react": "^16.6.0",
38+ "react": "^16.8.6",
40− "react-dom": "^16.6.0",
39+ "react-dom": "^16.8.6",
41− "react-router-dom": "^4.3.1",
42 "react-scripts": "^2.1.1",
40 "react-scripts": "^2.1.1",
43 "secp256k1": "^3.5.2",
41 "secp256k1": "^3.5.2",
44 "sigil-js": "github:urbit/sigil-js#f7f29c5",
42 "sigil-js": "github:urbit/sigil-js#f7f29c5",
src/bridge/Bridge.js+101−152
@@ -1,24 +1,26 @@
1−import { Just, Nothing } from 'folktale/maybe';
1+import { Just, Nothing } from "folktale/maybe";
2−import * as lodash from 'lodash';
2+import { includes } from "lodash";
3−import * as azimuth from 'azimuth-js';
3+import * as azimuth from "azimuth-js";
4−import { Stack } from 'immutable';
5−import { CONTRACT_ADDRESSES } from './lib/contracts';
4+import { CONTRACT_ADDRESSES } from "./lib/contracts";
6−import React from 'react';
5+import React from "react";
7−
6+
8−import Web3 from 'web3';
7+import Web3 from "web3";
9−import Footer from './components/Footer';
8+import Footer from "./components/Footer";
10−import Header from './components/Header';
9+import Header from "./components/Header";
11−import { Container, Row, Col } from './components/Base';
10+import { Container, Row, Col } from "./components/Base";
12−
11+
13−import { ROUTE_NAMES, router } from './lib/router';
12+import { router } from "./lib/router";
13+import { ROUTE_NAMES } from "./lib/routeNames";
14+import { HistoryProvider, withHistory, useHistory } from "./lib/history";
15+import { TxnConfirmationsProvider } from "./store/txnConfirmations";
14−import { NETWORK_NAMES } from './lib/network';
16+import { NETWORK_NAMES } from "./lib/network";
15import {
17import {
16 WALLET_NAMES,
18 WALLET_NAMES,
17 DEFAULT_HD_PATH,
19 DEFAULT_HD_PATH,
18 walletFromMnemonic,
20 walletFromMnemonic,
19 addressFromSecp256k1Public,
21 addressFromSecp256k1Public,
20−} from './lib/wallet';
22+} from "./lib/wallet";
21−import { BRIDGE_ERROR } from './lib/error';
23+import { BRIDGE_ERROR } from "./lib/error";
22
24
23const initWeb3 = networkType => {
25const initWeb3 = networkType => {
24 if (networkType === NETWORK_NAMES.MAINNET) {
26 if (networkType === NETWORK_NAMES.MAINNET) {
@@ -31,7 +33,7 @@ const initWeb3 = networkType => {
31 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
33 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
32 return { web3: web3, contracts: contracts };
34 return { web3: web3, contracts: contracts };
33 } else if (networkType === NETWORK_NAMES.LOCAL) {
35 } else if (networkType === NETWORK_NAMES.LOCAL) {
34− const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
36+ const protocol = process.env.NODE_ENV === "development" ? "ws" : "wss";
35 const endpoint = `${protocol}://localhost:8545`;
37 const endpoint = `${protocol}://localhost:8545`;
36 const provider = new Web3.providers.WebsocketProvider(endpoint);
38 const provider = new Web3.providers.WebsocketProvider(endpoint);
37 const web3 = new Web3(provider);
39 const web3 = new Web3(provider);
@@ -40,12 +42,36 @@ const initWeb3 = networkType => {
40 }
42 }
41};
43};
42
44
45+export const View = function(props) {
46+ const history = useHistory();
47+ const Route = router(history.peek());
48+
49+ return <Route {...props} />;
50+};
51+
52+const VariableWidthColumn = withHistory(({ history, children }) => (
53+ // For the invite acceptance flow, widen the screen to use the full
54+ // container, and hide the breadcrumbs
55+ <Col
56+ className={
57+ history.includes(ROUTE_NAMES.INVITE_TICKET)
58+ ? "col-md-12"
59+ : "col-md-offset-1 col-md-10"
60+ }
61+ style={
62+ history.includes(ROUTE_NAMES.INVITE_TICKET) ? {} : { maxWidth: "620px" }
63+ }
64+ >
65+ {children}
66+ </Col>
67+));
68+
43class Bridge extends React.Component {
69class Bridge extends React.Component {
44 constructor(props) {
70 constructor(props) {
45 super(props);
71 super(props);
46
72
47 const networkType =
73 const networkType =
48− process.env.NODE_ENV === 'development'
74+ process.env.NODE_ENV === "development"
49 ? NETWORK_NAMES.LOCAL
75 ? NETWORK_NAMES.LOCAL
50 : NETWORK_NAMES.MAINNET;
76 : NETWORK_NAMES.MAINNET;
51
77
@@ -67,9 +93,6 @@ class Bridge extends React.Component {
67 // 'EthereumWallet'.
93 // 'EthereumWallet'.
68
94
69 this.state = {
95 this.state = {
70− // routes
71− routeCrumbs: Stack([ROUTE_NAMES.LANDING]),
72− routeData: {},
73 // network
96 // network
74 networkType: networkType,
97 networkType: networkType,
75 web3: Just(web3),
98 web3: Just(web3),
@@ -90,9 +113,6 @@ class Bridge extends React.Component {
90 txnConfirmations: {},
113 txnConfirmations: {},
91 };
114 };
92
115
93− this.pushRoute = this.pushRoute.bind(this);
94− this.popRoute = this.popRoute.bind(this);
95− this.skipRoute = this.skipRoute.bind(this);
96 this.setNetworkType = this.setNetworkType.bind(this);
116 this.setNetworkType = this.setNetworkType.bind(this);
97 this.setNetwork = this.setNetwork.bind(this);
117 this.setNetwork = this.setNetwork.bind(this);
98 this.setWalletType = this.setWalletType.bind(this);
118 this.setWalletType = this.setWalletType.bind(this);
@@ -102,24 +122,15 @@ class Bridge extends React.Component {
102 this.setAuthMnemonic = this.setAuthMnemonic.bind(this);
122 this.setAuthMnemonic = this.setAuthMnemonic.bind(this);
103 this.setPointCursor = this.setPointCursor.bind(this);
123 this.setPointCursor = this.setPointCursor.bind(this);
104 this.setTxnHashCursor = this.setTxnHashCursor.bind(this);
124 this.setTxnHashCursor = this.setTxnHashCursor.bind(this);
105− this.setTxnConfirmations = this.setTxnConfirmations.bind(this);
106 this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this);
125 this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this);
107 this.addToPointCache = this.addToPointCache.bind(this);
126 this.addToPointCache = this.addToPointCache.bind(this);
108 }
127 }
109
128
110 componentDidMount() {
129 componentDidMount() {
111− window.history.pushState(null, null, null);
112−
113− window.onpopstate = e => {
114− window.history.pushState(null, null, null);
115− this.popRoute();
116− };
117−
118 // NB (jtobin):
130 // NB (jtobin):
119 //
131 //
120 // If running in development, the following can be tweaked to get you set
132 // If running in development, the following can be tweaked to get you set
121 // up with a desirable initial state.
133 // up with a desirable initial state.
122−
123 // if (process.env.NODE_ENV === 'development') {
134 // if (process.env.NODE_ENV === 'development') {
124 //
135 //
125 // const socket = 'ws://localhost:8545'
136 // const socket = 'ws://localhost:8545'
@@ -152,34 +163,8 @@ class Bridge extends React.Component {
152 // }
163 // }
153 }
164 }
154
165
155− pushRoute(symbol, routeData) {
156− if (lodash.includes(ROUTE_NAMES, symbol)) {
157− this.setState((state, _) => ({
158− routeCrumbs: state.routeCrumbs.push(symbol),
159− routeData: routeData,
160− }));
161−
162− // Scroll to top of page with each route transition.
163− window.scrollTo(0, 0);
164− } else {
165− throw BRIDGE_ERROR.INVALID_ROUTE;
166− }
167− }
168−
169− skipRoute(len) {
170− this.setState((state, _) => ({
171− routeCrumbs: state.routeCrumbs.skip(len),
172− }));
173− }
174−
175− popRoute() {
176− this.setState((state, _) => ({
177− routeCrumbs: state.routeCrumbs.shift(),
178− }));
179− }
180−
181 setNetworkType(symbol) {
166 setNetworkType(symbol) {
182− if (lodash.includes(NETWORK_NAMES, symbol)) {
167+ if (includes(NETWORK_NAMES, symbol)) {
183 this.setState({
168 this.setState({
184 networkType: symbol,
169 networkType: symbol,
185 });
170 });
@@ -203,7 +188,7 @@ class Bridge extends React.Component {
203 }
188 }
204
189
205 setWalletType(symbol) {
190 setWalletType(symbol) {
206− if (lodash.includes(WALLET_NAMES, symbol)) {
191+ if (includes(WALLET_NAMES, symbol)) {
207 this.setState({
192 this.setState({
208 walletType: symbol,
193 walletType: symbol,
209 });
194 });
@@ -232,7 +217,7 @@ class Bridge extends React.Component {
232 wallet = walletFromMnemonic(
217 wallet = walletFromMnemonic(
233 mnemonic,
218 mnemonic,
234 DEFAULT_HD_PATH,
219 DEFAULT_HD_PATH,
235− urbitWallet.value.meta.passphrase
220+ urbitWallet.value.meta.passphrase,
236 );
221 );
237 wallet.map(wal => {
222 wallet.map(wal => {
238 wal.address = urbitWallet.value.ownership.keys.address;
223 wal.address = urbitWallet.value.ownership.keys.address;
@@ -260,23 +245,8 @@ class Bridge extends React.Component {
260 this.setState({ txnHashCursor });
245 this.setState({ txnHashCursor });
261 }
246 }
262
247
263− setTxnConfirmations(txnHash, txnConfirmations) {
264− this.setState((prevState, _) => {
265− return prevState.routeCrumbs.peek() === ROUTE_NAMES.SENT_TRANSACTION
266− ? {
267− txnConfirmations: {
268− ...prevState.txnConfirmations,
269− [txnHash]: txnConfirmations,
270− },
271− }
272− : null;
273− });
274− }
275−
276 render() {
248 render() {
277 const {
249 const {
278− routeCrumbs,
279− routeData,
280 networkType,
250 networkType,
281 walletType,
251 walletType,
282 walletHdPath,
252 walletHdPath,
@@ -288,87 +258,66 @@ class Bridge extends React.Component {
288 pointCursor,
258 pointCursor,
289 pointCache,
259 pointCache,
290 txnHashCursor,
260 txnHashCursor,
291− txnConfirmations,
292 web3,
261 web3,
293 contracts,
262 contracts,
294 } = this.state;
263 } = this.state;
295
264
296− const View = router(routeCrumbs.peek());
297−
298− // For the invite acceptance flow, widen the screen to use the full
299− // container, and hide the breadcrumbs
300− const colClass = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
301− ? 'col-md-12'
302− : 'col-md-offset-1 col-md-10';
303−
304− const colStyle = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
305− ? {}
306− : { maxWidth: '620px' };
307−
308− const showCrumbs = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
309− ? false
310− : true;
311−
312 return (
265 return (
313− <Container>
266+ <HistoryProvider initialRoutes={[{ name: ROUTE_NAMES.LANDING }]}>
314− <Row>
267+ <TxnConfirmationsProvider>
315− <Col className={colClass} style={colStyle}>
268+ <Container>
269+ <Row>
270+ <VariableWidthColumn>
316− <Header
271+ <Header
317− routeCrumbs={routeCrumbs}
318− skipRoute={this.skipRoute}
272+ skipRoute={this.skipRoute}
319− networkType={networkType}
273+ networkType={networkType}
320− wallet={wallet}
274+ wallet={wallet}
321− showCrumbs={showCrumbs}
322− pointCursor={pointCursor}
275+ pointCursor={pointCursor}
323− />
276+ />
324−
277+
325− <Row className={'row wrapper'}>
278+ <Row className={"row wrapper"}>
326− <View
279+ <View
327− // router
328− pushRoute={this.pushRoute}
329− popRoute={this.popRoute}
330− routeData={routeData}
331− // network
280+ // network
332− setNetworkType={this.setNetworkType}
281+ setNetworkType={this.setNetworkType}
333− setNetwork={this.setNetwork}
282+ setNetwork={this.setNetwork}
334− networkType={networkType}
283+ networkType={networkType}
335− web3={web3}
284+ web3={web3}
336− contracts={contracts}
285+ contracts={contracts}
337− // wallet
286+ // wallet
338− setWalletType={this.setWalletType}
287+ setWalletType={this.setWalletType}
339− setWalletHdPath={this.setWalletHdPath}
288+ setWalletHdPath={this.setWalletHdPath}
340− setWallet={this.setWallet}
289+ setWallet={this.setWallet}
341− walletType={walletType}
290+ walletType={walletType}
342− walletHdPath={walletHdPath}
291+ walletHdPath={walletHdPath}
343− wallet={wallet}
292+ wallet={wallet}
344− // urbit wallet
293+ // urbit wallet
345− urbitWallet={urbitWallet}
294+ urbitWallet={urbitWallet}
346− setUrbitWallet={this.setUrbitWallet}
295+ setUrbitWallet={this.setUrbitWallet}
347− authMnemonic={authMnemonic}
296+ authMnemonic={authMnemonic}
348− setAuthMnemonic={this.setAuthMnemonic}
297+ setAuthMnemonic={this.setAuthMnemonic}
349− // point
298+ // point
350− setPointCursor={this.setPointCursor}
299+ setPointCursor={this.setPointCursor}
351− addToPointCache={this.addToPointCache}
300+ addToPointCache={this.addToPointCache}
352− pointCursor={pointCursor}
301+ pointCursor={pointCursor}
353− pointCache={pointCache}
302+ pointCache={pointCache}
354− networkSeedCache={networkSeedCache}
303+ networkSeedCache={networkSeedCache}
355− networkRevisionCache={networkRevisionCache}
304+ networkRevisionCache={networkRevisionCache}
356− setNetworkSeedCache={this.setNetworkSeedCache}
305+ setNetworkSeedCache={this.setNetworkSeedCache}
357− // txn
306+ // txn
358− onSent={this.setTxnHashCursor}
307+ onSent={this.setTxnHashCursor}
359− setTxnHashCursor={this.setTxnHashCursor}
308+ setTxnHashCursor={this.setTxnHashCursor}
360− txnHashCursor={txnHashCursor}
309+ txnHashCursor={txnHashCursor}
361− setTxnConfirmations={this.setTxnConfirmations}
362− txnConfirmations={txnConfirmations}
363− />
310+ />
364−
311+
365− <div className={'push'} />
312+ <div className={"push"} />
313+ </Row>
314+
315+ <Footer />
316+ </VariableWidthColumn>
366 </Row>
317 </Row>
367−
318+ </Container>
368− <Footer />
319+ </TxnConfirmationsProvider>
369− </Col>
320+ </HistoryProvider>
370− </Row>
371− </Container>
372 );
321 );
373 }
322 }
374}
323}
src/bridge/components/Header.js+37−33
@@ -1,58 +1,62 @@
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 { getRouteBreadcrumb } from "../lib/router";
5+import { ROUTE_NAMES } from "../lib/routeNames";
6+import { useHistory } from "../lib/history";
5−import { isLast } from '../lib/lib';
7+import { isLast } from "../lib/lib";
6
8
7−const Crumbs = props => {
9+function Crumbs(props) {
8− const { routeCrumbs, skipRoute } = props;
9− const history = routeCrumbs.reverse();
10+ const history = useHistory();
10
11
11 // FIXME probably more straightforward to render them normally and just
12 // FIXME probably more straightforward to render them normally and just
12 // return the reversed array of renders
13 // return the reversed array of renders
13− const rendered = history.map((route, idx) => {
14+ const rendered = history.routes.map((route, idx) => {
14 return (
15 return (
15− <div className={'flex items-center'} key={`history-${idx}`}>
16+ <div className={"flex items-center"} key={`history-${idx}`}>
16 <Button
17 <Button
17− prop-type={'link'}
18+ prop-type={"link"}
18− prop-size={'sm'}
19+ prop-size={"sm"}
19 key={`history-button-${idx}`}
20 key={`history-button-${idx}`}
20− onClick={() => skipRoute(history.size - idx - 1)}>
21+ onClick={() => history.pop(history.size - idx - 1)}
22+ >
21− {renderRoute(props, route)}
23+ {getRouteBreadcrumb(props, route)}
22 </Button>
24 </Button>
23
25
24 {isLast(history.size, idx) ? (
26 {isLast(history.size, idx) ? (
25 <div />
27 <div />
26 ) : (
28 ) : (
27− <Chevron className={'h-4 mh-2'} />
29+ <Chevron className={"h-4 mh-2"} />
28 )}
30 )}
29 </div>
31 </div>
30 );
32 );
31 });
33 });
32
34
33 return rendered;
35 return rendered;
34−};
36+}
35−
37+
36−const Header = props => {
38+function Header(props) {
37− const crumbs = (
39+ const history = useHistory();
40+
41+ const showCrumbs = !history.includes(ROUTE_NAMES.INVITE_TICKET);
42+
43+ if (showCrumbs) {
44+ return (
38− <div className={'flex items-center h-10'}>
45+ <div className={"flex items-center h-10"}>
39− <Crumbs
46+ <Crumbs
40− routeCrumbs={props.routeCrumbs}
41− skipRoute={props.skipRoute}
42− networkType={props.networkType}
47+ networkType={props.networkType}
43− wallet={props.wallet}
48+ wallet={props.wallet}
44− pointCursor={props.pointCursor}
49+ pointCursor={props.pointCursor}
45− />
50+ />
46− </div>
51+ </div>
47− );
52+ );
53+ }
48
54
49− const noCrumbs = (
55+ return (
50− <h2 className="mt-9 clickable" onClick={() => props.skipRoute(1)}>
56+ <h2 className="mt-9 clickable" onClick={() => history.pop()}>
51 Bridge
57 Bridge
52 </h2>
58 </h2>
53 );
59 );
54−
55− return props.showCrumbs ? crumbs : noCrumbs;
56−};
60+}
57
61
58export default Header;
62export default Header;
src/bridge/components/OldHeader.js+30−26
@@ -1,8 +1,8 @@
1import React from 'react';
1import React from 'react';
2import { Button, Chevron } from '../components/Base';
2import { Button, Chevron } from '../components/Base';
3
3
4−import { renderRoute } from '../lib/router';
4+import { getRouteBreadcrumb } from '../lib/router'
5−import { isLast } from '../lib/lib';
5+import { isLast } from '../lib/lib'
6
6
7const Crumbs = props => {
7const Crumbs = props => {
8 const { routeCrumbs, skipRoute } = props;
8 const { routeCrumbs, skipRoute } = props;
@@ -11,30 +11,34 @@ const Crumbs = props => {
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+
14− return (
15+ return (
15− <div className={'flex items-center'} key={`history-${idx}`}>
16+ <div className={'flex items-center'} key={ `history-${idx}` }>
17+
16− <Button
18+ <Button
17− size={'s'}
19+ size={'s'}
18− type={'link'}
20+ type={'link'}
19− color={'black'}
21+ color={'black'}
20− key={`history-button-${idx}`}
22+ key={ `history-button-${idx}` }
21− onClick={() => skipRoute(history.size - idx - 1)}>
23+ onClick={ () => skipRoute(history.size - idx - 1) }
24+ >
22− {`${renderRoute(props, route)}`}
25+ { `${getRouteBreadcrumb(props, route)}` }
23− </Button>
26+ </Button>
24−
27+
28+ {
25− {isLast(history.size, idx) ? (
29+ isLast(history.size, idx)
26− <div />
30+ ? <div />
27− ) : (
28− <Chevron className={'h-4 mh-2'} />
31+ : <Chevron className={'h-4 mh-2'} />
29− )}
32+ }
33+
30− </div>
34+ </div>
31− );
35+ )
32− });
36+ })
33−
37+
34− return rendered;
38+ return rendered
35−};
39+}
36−
40+
37−const Header = props => (
41+const Header = (props) =>
38 <div className={'flex items-center h-8 pt-4'}>
42 <div className={'flex items-center h-8 pt-4'}>
39 <Crumbs
43 <Crumbs
40 routeCrumbs={props.routeCrumbs}
44 routeCrumbs={props.routeCrumbs}
src/bridge/components/RenderedPoint.js+18−15
@@ -1,12 +1,12 @@
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/routeNames";
9−import ReactSVGComponents from './ReactSVGComponents';
9+import ReactSVGComponents from "./ReactSVGComponents";
10
10
11const RenderedPoint = props => {
11const RenderedPoint = props => {
12 const { setPointCursor, routeHandler, point } = props;
12 const { setPointCursor, routeHandler, point } = props;
@@ -19,14 +19,15 @@ const RenderedPoint = props => {
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
24 key={`rendered-sigil-${point}`}
24 key={`rendered-sigil-${point}`}
25 className="clickable"
25 className="clickable"
26 onClick={() => {
26 onClick={() => {
27 setPointCursor(Maybe.Just(point));
27 setPointCursor(Maybe.Just(point));
28 routeHandler(ROUTE_NAMES.SHIP);
28 routeHandler(ROUTE_NAMES.SHIP);
29− }}>
29+ }}
30+ >
30 {sigil}
31 {sigil}
31 </div>
32 </div>
32 <div
33 <div
@@ -35,19 +36,21 @@ const RenderedPoint = props => {
35 onClick={() => {
36 onClick={() => {
36 setPointCursor(Maybe.Just(point));
37 setPointCursor(Maybe.Just(point));
37 routeHandler(ROUTE_NAMES.SHIP);
38 routeHandler(ROUTE_NAMES.SHIP);
38− }}>
39+ }}
40+ >
39 <code>{name}</code>
41 <code>{name}</code>
40 </div>
42 </div>
41 <div key={`rendered-deets-${point}`}>
43 <div key={`rendered-deets-${point}`}>
42 <Button
44 <Button
43 key={`rendered-deets-button-${point}`}
45 key={`rendered-deets-button-${point}`}
44− prop-type={'link'}
46+ prop-type={"link"}
45− prop-size={'sm'}
47+ prop-size={"sm"}
46 onClick={() => {
48 onClick={() => {
47 setPointCursor(Maybe.Just(point));
49 setPointCursor(Maybe.Just(point));
48 routeHandler(ROUTE_NAMES.SHIP);
50 routeHandler(ROUTE_NAMES.SHIP);
49− }}>
51+ }}
52+ >
50− {'Details →'}
53+ {"Details →"}
51 </Button>
54 </Button>
52 </div>
55 </div>
53 </Col>
56 </Col>
src/bridge/components/StatelessTransaction.js+105−96
@@ -1,16 +1,17 @@
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, InnerLabelDropdown } from './Base';
8+import { CheckboxButton, Input, InnerLabel, InnerLabelDropdown } from "./Base";
9−import { Warning } from '../components/Base';
9+import { Warning } from "../components/Base";
10
10
11−import { BRIDGE_ERROR } from '../lib/error';
11+import { BRIDGE_ERROR } from "../lib/error";
12−import { ROUTE_NAMES } from '../lib/router';
12+import { ROUTE_NAMES } from "../lib/routeNames";
13+import { withHistory } from "../lib/history";
13−import * as tank from '../lib/tank';
14+import * as tank from "../lib/tank";
14
15
15import {
16import {
16 sendSignedTransaction,
17 sendSignedTransaction,
@@ -19,13 +20,14 @@ import {
19 hexify,
20 hexify,
20 renderSignedTx,
21 renderSignedTx,
21 signTransaction,
22 signTransaction,
22−} from '../lib/txn';
23+} from "../lib/txn";
24+import { withTxnConfirmations } from "../store/txnConfirmations";
23
25
24const SUBMISSION_STATES = {
26const SUBMISSION_STATES = {
25− PROMPT: 'Send transaction',
27+ PROMPT: "Send transaction",
26− PREPARING: 'Preparing...',
28+ PREPARING: "Preparing...",
27− FUNDING: 'Finding transaction funding...',
29+ FUNDING: "Finding transaction funding...",
28− SENDING: 'Sending transaction...',
30+ SENDING: "Sending transaction...",
29};
31};
30
32
31class StatelessTransaction extends React.Component {
33class StatelessTransaction extends React.Component {
@@ -33,13 +35,13 @@ class StatelessTransaction extends React.Component {
33 super(props);
35 super(props);
34
36
35 this.state = {
37 this.state = {
36− gasPrice: '5',
38+ gasPrice: "5",
37− gasLimit: '600000',
39+ gasLimit: "600000",
38 showGasDetails: false,
40 showGasDetails: false,
39 userApproval: false,
41 userApproval: false,
40− chainId: '1',
42+ chainId: "1",
41 customChain: false,
43 customChain: false,
42− nonce: '0',
44+ nonce: "0",
43 stx: Nothing(),
45 stx: Nothing(),
44 txn: Nothing(),
46 txn: Nothing(),
45 txStatus: SUBMISSION_STATES.PROMPT,
47 txStatus: SUBMISSION_STATES.PROMPT,
@@ -80,7 +82,7 @@ class StatelessTransaction extends React.Component {
80 const txMetadata = {
82 const txMetadata = {
81 nonce: r[0],
83 nonce: r[0],
82 chainId: r[1],
84 chainId: r[1],
83− gasPrice: fromWei(r[2], 'gwei'),
85+ gasPrice: fromWei(r[2], "gwei"),
84 };
86 };
85
87
86 this.setState({ ...txMetadata });
88 this.setState({ ...txMetadata });
@@ -186,12 +188,12 @@ class StatelessTransaction extends React.Component {
186 try {
188 try {
187 this.setState({ txStatus: SUBMISSION_STATES.PREPARING });
189 this.setState({ txStatus: SUBMISSION_STATES.PREPARING });
188 const res = await props.beforeSend(stx);
190 const res = await props.beforeSend(stx);
189− if (res === false) throw new Error('beforeSend disallowed sending');
191+ if (res === false) throw new Error("beforeSend disallowed sending");
190 } catch (e) {
192 } catch (e) {
191− console.log('beforeSend error', e);
193+ console.log("beforeSend error", e);
192 this.setState({
194 this.setState({
193 txStatus: SUBMISSION_STATES.PROMPT,
195 txStatus: SUBMISSION_STATES.PROMPT,
194− txError: Just('Something went wrong!'),
196+ txError: Just("Something went wrong!"),
195 });
197 });
196 return;
198 return;
197 }
199 }
@@ -200,7 +202,7 @@ class StatelessTransaction extends React.Component {
200 this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
202 this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
201
203
202 const rawTx = hexify(stx.serialize());
204 const rawTx = hexify(stx.serialize());
203− const cost = state.gasLimit * toWei(state.gasPrice, 'gwei');
205+ const cost = state.gasLimit * toWei(state.gasPrice, "gwei");
204
206
205 const address = need.address(props);
207 const address = need.address(props);
206 let balance = await web3.eth.getBalance(address);
208 let balance = await web3.eth.getBalance(address);
@@ -230,18 +232,18 @@ class StatelessTransaction extends React.Component {
230 web3,
232 web3,
231 state.stx,
233 state.stx,
232 usedTank,
234 usedTank,
233− props.setTxnConfirmations
235+ props.setTxnConfirmations,
234 )
236 )
235 .then(txHash => {
237 .then(txHash => {
236 props.onSent(Just(Ok(txHash)), state.stx.value);
238 props.onSent(Just(Ok(txHash)), state.stx.value);
237− props.popRoute();
239+ props.history.pop();
238
240
239 let routeData = {};
241 let routeData = {};
240 if (props.networkSeed) {
242 if (props.networkSeed) {
241 props.setNetworkSeedCache(props.networkSeed);
243 props.setNetworkSeedCache(props.networkSeed);
242 routeData.promptKeyfile = true;
244 routeData.promptKeyfile = true;
243 }
245 }
244− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, routeData);
246+ props.history.push(ROUTE_NAMES.SENT_TRANSACTION, routeData);
245 })
247 })
246 .catch(err => {
248 .catch(err => {
247 this.setState({
249 this.setState({
@@ -269,13 +271,13 @@ class StatelessTransaction extends React.Component {
269
271
270 getChainTitle(chainId) {
272 getChainTitle(chainId) {
271 let map = {
273 let map = {
272− '1': 'Mainnet - 1',
274+ "1": "Mainnet - 1",
273− '2': 'Morden - 2',
275+ "2": "Morden - 2",
274− '3': 'Ropsten - 3',
276+ "3": "Ropsten - 3",
275− '4': 'Goerli - 4',
277+ "4": "Goerli - 4",
276− '42': 'Kovan - 42',
278+ "42": "Kovan - 42",
277− '1337': 'Geth private chains - 1337',
279+ "1337": "Geth private chains - 1337",
278− custom: 'Custom',
280+ custom: "Custom",
279 };
281 };
280
282
281 return map[chainId];
283 return map[chainId];
@@ -284,32 +286,32 @@ class StatelessTransaction extends React.Component {
284 getChainOptions() {
286 getChainOptions() {
285 return [
287 return [
286 {
288 {
287− title: this.getChainTitle('1'),
289+ title: this.getChainTitle("1"),
288− value: '1',
290+ value: "1",
289 },
291 },
290 {
292 {
291− title: this.getChainTitle('2'),
293+ title: this.getChainTitle("2"),
292− value: '2',
294+ value: "2",
293 },
295 },
294 {
296 {
295− title: this.getChainTitle('3'),
297+ title: this.getChainTitle("3"),
296− value: '3',
298+ value: "3",
297 },
299 },
298 {
300 {
299− title: this.getChainTitle('4'),
301+ title: this.getChainTitle("4"),
300− value: '4',
302+ value: "4",
301 },
303 },
302 {
304 {
303− title: this.getChainTitle('42'),
305+ title: this.getChainTitle("42"),
304− value: '42',
306+ value: "42",
305 },
307 },
306 {
308 {
307− title: this.getChainTitle('1337'),
309+ title: this.getChainTitle("1337"),
308− value: '1337',
310+ value: "1337",
309 },
311 },
310 {
312 {
311− title: this.getChainTitle('custom'),
313+ title: this.getChainTitle("custom"),
312− value: 'custom',
314+ value: "custom",
313 },
315 },
314 ];
316 ];
315 }
317 }
@@ -349,26 +351,27 @@ class StatelessTransaction extends React.Component {
349
351
350 const chainOptions = this.getChainOptions();
352 const chainOptions = this.getChainOptions();
351
353
352− const generateButtonColor = Nothing.hasInstance(txn) ? 'blue' : 'green';
354+ const generateButtonColor = Nothing.hasInstance(txn) ? "blue" : "green";
353
355
354− const signerButtonColor = Nothing.hasInstance(stx) ? 'blue' : 'green';
356+ const signerButtonColor = Nothing.hasInstance(stx) ? "blue" : "green";
355
357
356 const generateTxnButton = (
358 const generateTxnButton = (
357 <Button
359 <Button
358− className={'mt-8'}
360+ className={"mt-8"}
359 disabled={!canGenerate}
361 disabled={!canGenerate}
360 prop-color={generateButtonColor}
362 prop-color={generateButtonColor}
361− prop-size={'lg wide'}
363+ prop-size={"lg wide"}
362− onClick={createUnsignedTxn}>
364+ onClick={createUnsignedTxn}
365+ >
363− {'Generate Transaction'}
366+ {"Generate Transaction"}
364 </Button>
367 </Button>
365 );
368 );
366
369
367 const unsignedTxnDisplay = txn.matchWith({
370 const unsignedTxnDisplay = txn.matchWith({
368− Nothing: _ => '',
371+ Nothing: _ => "",
369 Just: tx => (
372 Just: tx => (
370 <React.Fragment>
373 <React.Fragment>
371− <H3 className={'mt-8'}>{'Unsigned Transaction'}</H3>
374+ <H3 className={"mt-8"}>{"Unsigned Transaction"}</H3>
372 <Code>{JSON.stringify(tx.value, null, 2)}</Code>
375 <Code>{JSON.stringify(tx.value, null, 2)}</Code>
373 </React.Fragment>
376 </React.Fragment>
374 ),
377 ),
@@ -414,45 +417,48 @@ class StatelessTransaction extends React.Component {
414
417
415 const gasPriceDialogue = (
418 const gasPriceDialogue = (
416 <Input
419 <Input
417− className={'mono mt-4'}
420+ className={"mono mt-4"}
418− prop-size={'md'}
421+ prop-size={"md"}
419− prop-format={'innerLabel'}
422+ prop-format={"innerLabel"}
420 value={gasPrice}
423 value={gasPrice}
421− onChange={setGasPrice}>
424+ onChange={setGasPrice}
425+ >
422− <InnerLabel>{'Gas Price (gwei)'}</InnerLabel>
426+ <InnerLabel>{"Gas Price (gwei)"}</InnerLabel>
423 </Input>
427 </Input>
424 );
428 );
425
429
426 const gasLimitDialogue = (
430 const gasLimitDialogue = (
427 <Input
431 <Input
428− className={'mono mt-4'}
432+ className={"mono mt-4"}
429− prop-size={'md'}
433+ prop-size={"md"}
430− prop-format={'innerLabel'}
434+ prop-format={"innerLabel"}
431 value={gasLimit}
435 value={gasLimit}
432− onChange={setGasLimit}>
436+ onChange={setGasLimit}
437+ >
433− <InnerLabel>{'Gas Limit'}</InnerLabel>
438+ <InnerLabel>{"Gas Limit"}</InnerLabel>
434 </Input>
439 </Input>
435 );
440 );
436
441
437 const nonceDialogue = (
442 const nonceDialogue = (
438 <Input
443 <Input
439− className={'mono mt-4 mb-4'}
444+ className={"mono mt-4 mb-4"}
440− prop-size={'md'}
445+ prop-size={"md"}
441− prop-format={'innerLabel'}
446+ prop-format={"innerLabel"}
442 value={nonce}
447 value={nonce}
443− onChange={setNonce}>
448+ onChange={setNonce}
449+ >
444− <InnerLabel>{'Nonce'}</InnerLabel>
450+ <InnerLabel>{"Nonce"}</InnerLabel>
445 </Input>
451 </Input>
446 );
452 );
447
453
448 const chainDialogueTitle = customChain
454 const chainDialogueTitle = customChain
449− ? 'Custom'
455+ ? "Custom"
450 : this.getChainTitle(chainId);
456 : this.getChainTitle(chainId);
451 const chainDialogue = (
457 const chainDialogue = (
452 <InnerLabelDropdown
458 <InnerLabelDropdown
453− className={'mt-6'}
459+ className={"mt-6"}
454 fullWidth={true}
460 fullWidth={true}
455− title={'Chain ID'}
461+ title={"Chain ID"}
456 options={chainOptions}
462 options={chainOptions}
457 handleUpdate={handleChainUpdate}
463 handleUpdate={handleChainUpdate}
458 currentSelectionTitle={chainDialogueTitle}
464 currentSelectionTitle={chainDialogueTitle}
@@ -461,12 +467,13 @@ class StatelessTransaction extends React.Component {
461
467
462 const customChainDialogue = !customChain ? null : (
468 const customChainDialogue = !customChain ? null : (
463 <Input
469 <Input
464− className={'mono mt-4 mb-8'}
470+ className={"mono mt-4 mb-8"}
465− prop-size={'md'}
471+ prop-size={"md"}
466− prop-format={'innerLabel'}
472+ prop-format={"innerLabel"}
467 value={chainId}
473 value={chainId}
468− onChange={setChainId}>
474+ onChange={setChainId}
475+ >
469− <InnerLabel>{'Chain ID'}</InnerLabel>
476+ <InnerLabel>{"Chain ID"}</InnerLabel>
470 </Input>
477 </Input>
471 );
478 );
472
479
@@ -484,21 +491,22 @@ class StatelessTransaction extends React.Component {
484 const signTxnButton = (
491 const signTxnButton = (
485 <Button
492 <Button
486 disabled={!canSign}
493 disabled={!canSign}
487− className={'mt-8'}
494+ className={"mt-8"}
488− prop-size={'lg wide'}
495+ prop-size={"lg wide"}
489 prop-color={signerButtonColor}
496 prop-color={signerButtonColor}
490 onClick={() =>
497 onClick={() =>
491 signTransaction({ ...this.props, ...this.state, setStx: this.setStx })
498 signTransaction({ ...this.props, ...this.state, setStx: this.setStx })
492− }>
499+ }
500+ >
493− {'Sign Transaction'}
501+ {"Sign Transaction"}
494 </Button>
502 </Button>
495 );
503 );
496
504
497 const signedTxnDisplay = stx.matchWith({
505 const signedTxnDisplay = stx.matchWith({
498− Nothing: _ => '',
506+ Nothing: _ => "",
499 Just: tx => (
507 Just: tx => (
500 <React.Fragment>
508 <React.Fragment>
501− <H3 className={'mt-8'}>{'Signed Transaction'}</H3>
509+ <H3 className={"mt-8"}>{"Signed Transaction"}</H3>
502 <Code>{JSON.stringify(renderSignedTx(tx.value), null, 2)}</Code>
510 <Code>{JSON.stringify(renderSignedTx(tx.value), null, 2)}</Code>
503 </React.Fragment>
511 </React.Fragment>
504 ),
512 ),
@@ -506,7 +514,7 @@ class StatelessTransaction extends React.Component {
506
514
507 const confirmButton = (
515 const confirmButton = (
508 <CheckboxButton
516 <CheckboxButton
509− className={'mt-8'}
517+ className={"mt-8"}
510 disabled={!canApprove}
518 disabled={!canApprove}
511 onToggle={setUserApproval}
519 onToggle={setUserApproval}
512 state={userApproval}
520 state={userApproval}
@@ -515,13 +523,14 @@ class StatelessTransaction extends React.Component {
515 );
523 );
516
524
517 const sending = txStatus !== SUBMISSION_STATES.PROMPT;
525 const sending = txStatus !== SUBMISSION_STATES.PROMPT;
518− const sendTxnSpinner = sending ? '' : 'hide';
526+ const sendTxnSpinner = sending ? "" : "hide";
519 const sendTxnButton = (
527 const sendTxnButton = (
520 <Button
528 <Button
521− prop-size={'xl wide'}
529+ prop-size={"xl wide"}
522− className={'mt-8'}
530+ className={"mt-8"}
523 disabled={!canSend || sending}
531 disabled={!canSend || sending}
524− onClick={sendTxn}>
532+ onClick={sendTxn}
533+ >
525 <span className="relative">
534 <span className="relative">
526 <span className={`btn-spinner ${sendTxnSpinner}`} />
535 <span className={`btn-spinner ${sendTxnSpinner}`} />
527 {txStatus}
536 {txStatus}
@@ -530,7 +539,7 @@ class StatelessTransaction extends React.Component {
530 );
539 );
531
540
532 const sendDialogue = web3.matchWith({
541 const sendDialogue = web3.matchWith({
533− Nothing: _ => '',
542+ Nothing: _ => "",
534 Just: _ => (
543 Just: _ => (
535 <React.Fragment>
544 <React.Fragment>
536 {confirmButton}
545 {confirmButton}
@@ -540,11 +549,11 @@ class StatelessTransaction extends React.Component {
540 });
549 });
541
550
542 const txnErrorDialogue = Nothing.hasInstance(state.txError) ? (
551 const txnErrorDialogue = Nothing.hasInstance(state.txError) ? (
543− ''
552+ ""
544 ) : (
553 ) : (
545− <Warning className={'mt-8'}>
554+ <Warning className={"mt-8"}>
546 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
555 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
547− {'There was an error sending your transaction.'}
556+ {"There was an error sending your transaction."}
548 </H3>
557 </H3>
549 {state.txError.value}
558 {state.txError.value}
550 </Warning>
559 </Warning>
@@ -577,4 +586,4 @@ class StatelessTransaction extends React.Component {
577 }
586 }
578}
587}
579
588
580−export default StatelessTransaction;
589+export default withTxnConfirmations(withHistory(StatelessTransaction));
src/bridge/lib/history.jsnew file+60
@@ -0,0 +1,60 @@
1+import React, { useContext, useState, useEffect } from "react";
2+import { last, includes } from "lodash";
3+
4+import { BRIDGE_ERROR } from "./error";
5+import { ROUTE_NAMES } from "./routeNames";
6+
7+export const HistoryContext = React.createContext(null);
8+
9+export function HistoryProvider({ initialRoutes = [], children }) {
10+ const [history, setHistory] = useState(initialRoutes);
11+
12+ const value = {
13+ routes: history,
14+ push: (name, data) => {
15+ if (!includes(ROUTE_NAMES, name)) {
16+ throw BRIDGE_ERROR.INVALID_ROUTE;
17+ }
18+
19+ setHistory([...history, { name, data }]);
20+ },
21+ popAndPush: (name, data = {}) =>
22+ setHistory([...history.slice(0, history.length - 1), { name, data }]),
23+ pop: (count = 1) =>
24+ setHistory([...history.slice(0, history.length - count)]),
25+ peek: () => last(history),
26+ size: history.length,
27+ includes: name => includes(history.map(r => r.name), name),
28+ data: () => last(history).data,
29+ };
30+
31+ // Scroll to top of page with each route transition.
32+ useEffect(() => {
33+ window.scrollTo(0, 0);
34+ }, [history]);
35+
36+ useEffect(() => {
37+ window.history.pushState(null, null, null);
38+
39+ window.onpopstate = e => {
40+ window.history.pushState(null, null, null);
41+ value.pop();
42+ };
43+ });
44+
45+ return (
46+ <HistoryContext.Provider value={value}>{children}</HistoryContext.Provider>
47+ );
48+}
49+
50+// HOC version
51+export const withHistory = Component => props => (
52+ <HistoryContext.Consumer>
53+ {history => <Component history={history} {...props} />}
54+ </HistoryContext.Consumer>
55+);
56+
57+// Hook version
58+export function useHistory() {
59+ return useContext(HistoryContext);
60+}
src/bridge/lib/invite.js+67−67
@@ -1,67 +1,67 @@
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 "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
@@ -77,11 +77,11 @@ async function downloadWallet(paper) {
77 const zip = new JSZip();
77 const zip = new JSZip();
78 //TODO the categories here aren't explained in bridge at all...
78 //TODO the categories here aren't explained in bridge at all...
79
79
80− const bin0 = paper.filter(item => item.bin === '0');
80+ const bin0 = paper.filter(item => item.bin === "0");
81− const bin1 = paper.filter(item => item.bin === '1');
81+ const bin1 = paper.filter(item => item.bin === "1");
82− const bin2 = paper.filter(item => item.bin === '2');
82+ const bin2 = paper.filter(item => item.bin === "2");
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');
@@ -95,8 +95,8 @@ async function downloadWallet(paper) {
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 });
@@ -115,7 +115,7 @@ async function startTransactions(args) {
115
115
116 const askForFunding = (address, amount, current) => {
116 const askForFunding = (address, amount, current) => {
117 updateProgress({
117 updateProgress({
118− type: 'notify',
118+ type: "notify",
119 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`,
120 });
120 });
121 };
121 };
@@ -148,14 +148,14 @@ async function startTransactions(args) {
148 const point = realPointM.value;
148 const point = realPointM.value;
149
149
150 const inviteAddress = inviteWallet.address;
150 const inviteAddress = inviteWallet.address;
151− console.log('working as', inviteAddress);
151+ console.log("working as", inviteAddress);
152
152
153 // transfer from invite wallet to new wallet
153 // transfer from invite wallet to new wallet
154
154
155 let transferTx = azimuth.ecliptic.transferPoint(
155 let transferTx = azimuth.ecliptic.transferPoint(
156 contracts,
156 contracts,
157 point,
157 point,
158− realWallet.ownership.keys.address
158+ realWallet.ownership.keys.address,
159 );
159 );
160 transferTx.gas = 500000; //TODO can maybe be lower?
160 transferTx.gas = 500000; //TODO can maybe be lower?
161
161
@@ -169,11 +169,11 @@ async function startTransactions(args) {
169 //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)
170 let transferStx = new Tx(transferTx);
170 let transferStx = new Tx(transferTx);
171 transferStx.sign(inviteWallet.privateKey);
171 transferStx.sign(inviteWallet.privateKey);
172− let rawTransferStx = '0x' + transferStx.serialize().toString('hex');
172+ let rawTransferStx = "0x" + transferStx.serialize().toString("hex");
173
173
174 const transferCost = transferTx.gas * transferTx.gasPrice;
174 const transferCost = transferTx.gas * transferTx.gasPrice;
175 updateProgress({
175 updateProgress({
176− type: 'progress',
176+ type: "progress",
177 value: TRANSACTION_STATES.FUNDING_INVITE,
177 value: TRANSACTION_STATES.FUNDING_INVITE,
178 });
178 });
179 let usedTank = await tank.ensureFundsFor(
179 let usedTank = await tank.ensureFundsFor(
@@ -182,11 +182,11 @@ async function startTransactions(args) {
182 inviteAddress,
182 inviteAddress,
183 transferCost,
183 transferCost,
184 [rawTransferStx],
184 [rawTransferStx],
185− askForFunding
185+ askForFunding,
186 );
186 );
187
187
188 updateProgress({
188 updateProgress({
189− type: 'progress',
189+ type: "progress",
190 value: TRANSACTION_STATES.CLAIMING,
190 value: TRANSACTION_STATES.CLAIMING,
191 });
191 });
192
192
@@ -205,7 +205,7 @@ async function startTransactions(args) {
205 let managementTx = azimuth.ecliptic.setManagementProxy(
205 let managementTx = azimuth.ecliptic.setManagementProxy(
206 contracts,
206 contracts,
207 point,
207 point,
208− realWallet.management.keys.address
208+ realWallet.management.keys.address,
209 );
209 );
210 managementTx.gas = 200000;
210 managementTx.gas = 200000;
211 managementTx.nonce = 0;
211 managementTx.nonce = 0;
@@ -219,7 +219,7 @@ async function startTransactions(args) {
219 pointCache: { [point]: { keyRevisionNumber: 0 } },
219 pointCache: { [point]: { keyRevisionNumber: 0 } },
220 });
220 });
221 if (Nothing.hasInstance(networkSeed)) {
221 if (Nothing.hasInstance(networkSeed)) {
222− throw new Error('wtf network seed not derived');
222+ throw new Error("wtf network seed not derived");
223 }
223 }
224 networkSeed = networkSeed.value;
224 networkSeed = networkSeed.value;
225 const networkKeys = kg.deriveNetworkKeys(networkSeed);
225 const networkKeys = kg.deriveNetworkKeys(networkSeed);
@@ -230,7 +230,7 @@ async function startTransactions(args) {
230 addHexPrefix(networkKeys.crypt.public),
230 addHexPrefix(networkKeys.crypt.public),
231 addHexPrefix(networkKeys.auth.public),
231 addHexPrefix(networkKeys.auth.public),
232 1,
232 1,
233− false
233+ false,
234 );
234 );
235 keysTx.gas = 150000;
235 keysTx.gas = 150000;
236 keysTx.nonce = 1;
236 keysTx.nonce = 1;
@@ -247,12 +247,12 @@ async function startTransactions(args) {
247
247
248 let rawStxs = txs.map(tx => {
248 let rawStxs = txs.map(tx => {
249 let stx = new Tx(tx);
249 let stx = new Tx(tx);
250− stx.sign(Buffer.from(realWallet.ownership.keys.private, 'hex'));
250+ stx.sign(Buffer.from(realWallet.ownership.keys.private, "hex"));
251− return '0x' + stx.serialize().toString('hex');
251+ return "0x" + stx.serialize().toString("hex");
252 });
252 });
253
253
254 updateProgress({
254 updateProgress({
255− type: 'progress',
255+ type: "progress",
256 value: TRANSACTION_STATES.FUNDING_RECIPIENT,
256 value: TRANSACTION_STATES.FUNDING_RECIPIENT,
257 });
257 });
258 usedTank = await tank.ensureFundsFor(
258 usedTank = await tank.ensureFundsFor(
@@ -261,18 +261,18 @@ async function startTransactions(args) {
261 newAddress,
261 newAddress,
262 totalCost,
262 totalCost,
263 rawStxs,
263 rawStxs,
264− askForFunding
264+ askForFunding,
265 );
265 );
266
266
267 updateProgress({
267 updateProgress({
268− type: 'progress',
268+ type: "progress",
269 value: TRANSACTION_STATES.CONFIGURING,
269 value: TRANSACTION_STATES.CONFIGURING,
270 });
270 });
271
271
272 await sendAndAwaitConfirm(web3, rawStxs, usedTank);
272 await sendAndAwaitConfirm(web3, rawStxs, usedTank);
273
273
274 updateProgress({
274 updateProgress({
275− type: 'progress',
275+ type: "progress",
276 value: TRANSACTION_STATES.CLEANING,
276 value: TRANSACTION_STATES.CLEANING,
277 });
277 });
278
278
@@ -283,7 +283,7 @@ async function startTransactions(args) {
283 const sendEthCost = gasPrice * gasLimit;
283 const sendEthCost = gasPrice * gasLimit;
284 if (balance > sendEthCost) {
284 if (balance > sendEthCost) {
285 const value = balance - sendEthCost;
285 const value = balance - sendEthCost;
286− console.log('sending', value);
286+ console.log("sending", value);
287 const tx = {
287 const tx = {
288 to: newAddress,
288 to: newAddress,
289 value: value,
289 value: value,
@@ -293,15 +293,15 @@ async function startTransactions(args) {
293 };
293 };
294 let stx = new Tx(tx);
294 let stx = new Tx(tx);
295 stx.sign(inviteWallet.privateKey);
295 stx.sign(inviteWallet.privateKey);
296− const rawTx = '0x' + stx.serialize().toString('hex');
296+ const rawTx = "0x" + stx.serialize().toString("hex");
297 web3.eth.sendSignedTransaction(rawTx).catch(err => {
297 web3.eth.sendSignedTransaction(rawTx).catch(err => {
298− console.log('error sending value tx, who cares', err);
298+ console.log("error sending value tx, who cares", err);
299 });
299 });
300− console.log('sent old balance');
300+ console.log("sent old balance");
301 }
301 }
302
302
303 updateProgress({
303 updateProgress({
304− type: 'progress',
304+ type: "progress",
305 value: TRANSACTION_STATES.DONE,
305 value: TRANSACTION_STATES.DONE,
306 });
306 });
307
307
@@ -315,7 +315,7 @@ async function sendAndAwaitConfirm(web3, signedTxs, usedTank) {
315 return new Promise((resolve, reject) => {
315 return new Promise((resolve, reject) => {
316 sendSignedTransaction(web3, tx, usedTank, resolve);
316 sendSignedTransaction(web3, tx, usedTank, resolve);
317 });
317 });
318− })
318+ }),
319 );
319 );
320}
320}
321
321
src/bridge/lib/keys.js+21−29
@@ -1,18 +1,18 @@
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 "urbit-key-generation/dist/index";
8
8
9−import { BRIDGE_ERROR } from './error';
9+import { BRIDGE_ERROR } from "./error";
10−import { WALLET_NAMES, eqAddr } from './wallet';
10+import { WALLET_NAMES, eqAddr } from "./wallet";
11
11
12// ctsy joemfb
12// ctsy joemfb
13const b64 = buf => {
13const b64 = buf => {
14− let hex = buf.reverse().toString('hex');
14+ let hex = buf.reverse().toString("hex");
15− let n = new BN(hex, 'hex');
15+ let n = new BN(hex, "hex");
16 let c = [];
16 let c = [];
17 while (1 === n.cmpn(0)) {
17 while (1 === n.cmpn(0)) {
18 c.push(n.andln(0x3f));
18 c.push(n.andln(0x3f));
@@ -21,22 +21,14 @@ const b64 = buf => {
21
21
22 // prettier-ignore
22 // prettier-ignore
23 const trans = j =>
23 const trans = j =>
24− 10 > j
24+ 10 > j ? j + 48 : 36 > j ? j + 87 : 62 > j ? j + 29 : 62 === j ? 45 : 126;
25− ? j + 48
26− : 36 > j
27− ? j + 87
28− : 62 > j
29− ? j + 29
30− : 62 === j
31− ? 45
32− : 126
33
25
34 return (
26 return (
35− '0w' +
27+ "0w" +
36 c.reduce(
28 c.reduce(
37 (a, b, i) =>
29 (a, b, i) =>
38− String.fromCharCode(trans(b)) + (i && 0 === i % 5 ? '.' : '') + a,
30+ String.fromCharCode(trans(b)) + (i && 0 === i % 5 ? "." : "") + a,
39− ''
31+ "",
40 )
32 )
41 );
33 );
42};
34};
@@ -46,19 +38,19 @@ const jam = seed => {
46 .jam(seed)
38 .jam(seed)
47 .toString()
39 .toString()
48 .slice(2);
40 .slice(2);
49− const pad = hex.length % 2 === 0 ? hex : '0' + hex;
41+ const pad = hex.length % 2 === 0 ? hex : "0" + hex;
50− return Buffer.from(pad, 'hex').reverse();
42+ return Buffer.from(pad, "hex").reverse();
51};
43};
52
44
53const genKey = (networkSeed, point, revision) => {
45const genKey = (networkSeed, point, revision) => {
54 const pair = kg.deriveNetworkKeys(networkSeed);
46 const pair = kg.deriveNetworkKeys(networkSeed);
55− const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex'); // '42' is curve parameter
47+ const bnsec = new BN(pair.crypt.private + pair.auth.private + "42", "hex"); // '42' is curve parameter
56
48
57 const sed = noun.dwim(
49 const sed = noun.dwim(
58 noun.Atom.fromString(point),
50 noun.Atom.fromString(point),
59 noun.Atom.fromInt(revision),
51 noun.Atom.fromInt(revision),
60 noun.Atom.fromString(bnsec.toString()),
52 noun.Atom.fromString(bnsec.toString()),
61− noun.Atom.yes
53+ noun.Atom.yes,
62 );
54 );
63
55
64 return b64(jam(sed));
56 return b64(jam(sed));
@@ -82,7 +74,7 @@ const attemptSeedDerivation = async (next, args) => {
82 ? parseInt(pointDetails.keyRevisionNumber)
74 ? parseInt(pointDetails.keyRevisionNumber)
83 : parseInt(pointDetails.keyRevisionNumber) - 1;
75 : parseInt(pointDetails.keyRevisionNumber) - 1;
84
76
85− let managementSeed = '';
77+ let managementSeed = "";
86
78
87 const ticketLike = [WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS];
79 const ticketLike = [WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS];
88
80
@@ -118,8 +110,8 @@ const attemptSeedDerivation = async (next, args) => {
118
110
119 let networkSeed = Maybe.Nothing();
111 let networkSeed = Maybe.Nothing();
120
112
121− if (managementSeed !== '') {
113+ if (managementSeed !== "") {
122− const seed = await kg.deriveNetworkSeed(managementSeed, '', revision);
114+ const seed = await kg.deriveNetworkSeed(managementSeed, "", revision);
123
115
124 networkSeed = Maybe.Just(seed);
116 networkSeed = Maybe.Just(seed);
125 }
117 }
src/bridge/lib/routeNames.jsnew file+33
@@ -0,0 +1,33 @@
1+export const ROUTE_NAMES = {
2+ DEFAULT: Symbol("DEFAULT"),
3+ //
4+ INVITE_TICKET: Symbol("INVITE_TICKET"),
5+ INVITE_TRANSACTIONS: Symbol("INVITE_TRANSACTIONS"),
6+ INVITES_SEND: Symbol("INVITES_SEND"),
7+ INVITES_MANAGE: Symbol("INVITES_MANAGE"),
8+ //
9+ ACCEPT_TRANSFER: Symbol("ACCEPT_TRANSFER"),
10+ CANCEL_TRANSFER: Symbol("CANCEL_TRANSFER"),
11+ LANDING: Symbol("LANDING"),
12+ NETWORK: Symbol("NETWORK"),
13+ WALLET: Symbol("WALLET"),
14+ MNEMONIC: Symbol("MNEMONIC"),
15+ TICKET: Symbol("TICKET"),
16+ SHARDS: Symbol("SHARDS"),
17+ LEDGER: Symbol("LEDGER"),
18+ TREZOR: Symbol("TREZOR"),
19+ PRIVATE_KEY: Symbol("PRIVATE_KEY"),
20+ KEYSTORE: Symbol("KEYSTORE"),
21+ VIEW_SHIP: Symbol("VIEW_SHIP"),
22+ SHIPS: Symbol("SHIPS"),
23+ SHIP: Symbol("SHIP"),
24+ SET_MANAGEMENT_PROXY: Symbol("SET_MANAGEMENT_PROXY"),
25+ SET_SPAWN_PROXY: Symbol("SET_SPAWN_PROXY"),
26+ SET_TRANSFER_PROXY: Symbol("SET_TRANSFER_PROXY"),
27+ CREATE_GALAXY: Symbol("CREATE_GALAXY"),
28+ ISSUE_CHILD: Symbol("ISSUE_CHILD"),
29+ SET_KEYS: Symbol("SET_KEYS"),
30+ TRANSFER: Symbol("TRANSFER"),
31+ SENT_TRANSACTION: Symbol("SENT_TRANSACTION"),
32+ GEN_KEYFILE: Symbol("GEN_KEYFILE"),
33+};
src/bridge/lib/router.js+122−164
@@ -1,182 +1,140 @@
1+import React from "react";
1−import * as ob from 'urbit-ob';
2+import * as ob from "urbit-ob";
2−import * as need from '../lib/need';
3+import * as need from "../lib/need";
3
4
4−import React from 'react';
5−
6−import Landing from '../views/Landing';
5+import Landing from "../views/Landing";
7−
8−import InviteTicket from '../views/InviteTicket';
6+import InviteTicket from "../views/InviteTicket";
9−
10−import InvitesSend from '../views/InvitesSend.js';
7+import InvitesSend from "../views/InvitesSend.js";
11−import InvitesManage from '../views/InvitesManage.js';
8+import InvitesManage from "../views/InvitesManage.js";
12−
13−import AcceptTransfer from '../views/AcceptTransfer';
9+import AcceptTransfer from "../views/AcceptTransfer";
14−import CancelTransfer from '../views/CancelTransfer';
10+import CancelTransfer from "../views/CancelTransfer";
15−import CreateGalaxy from '../views/CreateGalaxy';
11+import CreateGalaxy from "../views/CreateGalaxy";
16−import GenKeyfile from '../views/GenKeyfile';
12+import GenKeyfile from "../views/GenKeyfile";
17−import IssueChild from '../views/IssueChild';
13+import IssueChild from "../views/IssueChild";
18−import Network from '../views/Network';
14+import Network from "../views/Network";
19−import Mnemonic from '../views/Mnemonic';
15+import Mnemonic from "../views/Mnemonic";
20−import Ledger from '../views/Ledger';
16+import Ledger from "../views/Ledger";
21−import Trezor from '../views/Trezor';
17+import Trezor from "../views/Trezor";
22−import PrivateKey from '../views/PrivateKey';
18+import PrivateKey from "../views/PrivateKey";
23−import Keystore from '../views/Keystore';
19+import Keystore from "../views/Keystore";
24−import ViewPoint from '../views/ViewPoint';
20+import ViewPoint from "../views/ViewPoint";
25−import SentTransaction from '../views/SentTransaction';
21+import SentTransaction from "../views/SentTransaction";
26−import SetKeys from '../views/SetKeys';
22+import SetKeys from "../views/SetKeys";
27import {
23import {
28 SetManagementProxy,
24 SetManagementProxy,
29 SetSpawnProxy,
25 SetSpawnProxy,
30 SetTransferProxy,
26 SetTransferProxy,
31−} from '../views/SetProxy';
27+} from "../views/SetProxy";
32−import Point from '../views/Point';
28+import Point from "../views/Point";
33−import Points from '../views/Points';
29+import Points from "../views/Points";
34−import Shards from '../views/Shards';
30+import Shards from "../views/Shards";
35−import Ticket from '../views/Ticket';
31+import Ticket from "../views/Ticket";
36−import Transfer from '../views/Transfer';
32+import Transfer from "../views/Transfer";
37−import Wallet from '../views/Wallet';
33+import Wallet from "../views/Wallet";
38
34
39−import { EthereumWallet } from './wallet';
35+import { EthereumWallet } from "./wallet";
40−import { renderNetworkType } from './network';
36+import { renderNetworkType } from "./network";
37+import { ROUTE_NAMES } from "./routeNames";
41
38
42−const ROUTE_NAMES = {
39+const ROUTES = {
43− DEFAULT: Symbol('DEFAULT'),
40+ [ROUTE_NAMES.DEFAULT]: Landing,
44− //
41+ [ROUTE_NAMES.LANDING]: Landing,
45− INVITE_TICKET: Symbol('INVITE_TICKET'),
42+ [ROUTE_NAMES.INVITE_TICKET]: InviteTicket,
46− INVITE_TRANSACTIONS: Symbol('INVITE_TRANSACTIONS'),
43+ [ROUTE_NAMES.INVITES_SEND]: InvitesSend,
47− INVITES_SEND: Symbol('INVITES_SEND'),
44+ [ROUTE_NAMES.INVITES_MANAGE]: InvitesManage,
48− INVITES_MANAGE: Symbol('INVITES_MANAGE'),
45+ [ROUTE_NAMES.ACCEPT_TRANSFER]: AcceptTransfer,
49− //
46+ [ROUTE_NAMES.CANCEL_TRANSFER]: CancelTransfer,
50− ACCEPT_TRANSFER: Symbol('ACCEPT_TRANSFER'),
47+ [ROUTE_NAMES.NETWORK]: Network,
51− CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
48+ [ROUTE_NAMES.WALLET]: Wallet,
52− LANDING: Symbol('LANDING'),
49+ [ROUTE_NAMES.VIEW_SHIP]: ViewPoint,
53− NETWORK: Symbol('NETWORK'),
50+ [ROUTE_NAMES.MNEMONIC]: Mnemonic,
54− WALLET: Symbol('WALLET'),
51+ [ROUTE_NAMES.TICKET]: Ticket,
55− MNEMONIC: Symbol('MNEMONIC'),
52+ [ROUTE_NAMES.SHARDS]: Shards,
56− TICKET: Symbol('TICKET'),
53+ [ROUTE_NAMES.LEDGER]: Ledger,
57− SHARDS: Symbol('SHARDS'),
54+ [ROUTE_NAMES.TREZOR]: Trezor,
58− LEDGER: Symbol('LEDGER'),
55+ [ROUTE_NAMES.PRIVATE_KEY]: PrivateKey,
59− TREZOR: Symbol('TREZOR'),
56+ [ROUTE_NAMES.KEYSTORE]: Keystore,
60− PRIVATE_KEY: Symbol('PRIVATE_KEY'),
57+ [ROUTE_NAMES.SHIPS]: Points,
61− KEYSTORE: Symbol('KEYSTORE'),
58+ [ROUTE_NAMES.SHIP]: Point,
62− VIEW_SHIP: Symbol('VIEW_SHIP'),
63− SHIPS: Symbol('SHIPS'),
64− SHIP: Symbol('SHIP'),
65− SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
59+ [ROUTE_NAMES.SET_MANAGEMENT_PROXY]: SetManagementProxy,
66− SET_SPAWN_PROXY: Symbol('SET_SPAWN_PROXY'),
60+ [ROUTE_NAMES.SET_SPAWN_PROXY]: SetSpawnProxy,
67− SET_TRANSFER_PROXY: Symbol('SET_TRANSFER_PROXY'),
61+ [ROUTE_NAMES.SET_TRANSFER_PROXY]: SetTransferProxy,
68− CREATE_GALAXY: Symbol('CREATE_GALAXY'),
62+ [ROUTE_NAMES.CREATE_GALAXY]: CreateGalaxy,
69− ISSUE_CHILD: Symbol('ISSUE_CHILD'),
63+ [ROUTE_NAMES.ISSUE_CHILD]: IssueChild,
70− SET_KEYS: Symbol('SET_KEYS'),
64+ [ROUTE_NAMES.SET_KEYS]: SetKeys,
71− TRANSFER: Symbol('TRANSFER'),
65+ [ROUTE_NAMES.TRANSFER]: Transfer,
72− SENT_TRANSACTION: Symbol('SENT_TRANSACTION'),
66+ [ROUTE_NAMES.SENT_TRANSACTION]: SentTransaction,
73− GEN_KEYFILE: Symbol('GEN_KEYFILE'),
67+ [ROUTE_NAMES.GEN_KEYFILE]: GenKeyfile,
74};
68};
75
69
76−const createRoutes = () => {
70+export const getRouteBreadcrumb = (props, route) => {
77− const routes = {};
78− routes[ROUTE_NAMES.DEFAULT] = Landing;
79− //
80− routes[ROUTE_NAMES.INVITE_TICKET] = InviteTicket;
81− routes[ROUTE_NAMES.INVITES_SEND] = InvitesSend;
82− routes[ROUTE_NAMES.INVITES_MANAGE] = InvitesManage;
83− //
84− routes[ROUTE_NAMES.ACCEPT_TRANSFER] = AcceptTransfer;
85− routes[ROUTE_NAMES.CANCEL_TRANSFER] = CancelTransfer;
86− routes[ROUTE_NAMES.LANDING] = Landing;
87− routes[ROUTE_NAMES.NETWORK] = Network;
88− routes[ROUTE_NAMES.WALLET] = Wallet;
89− routes[ROUTE_NAMES.VIEW_SHIP] = ViewPoint;
90− routes[ROUTE_NAMES.MNEMONIC] = Mnemonic;
91− routes[ROUTE_NAMES.TICKET] = Ticket;
92− routes[ROUTE_NAMES.SHARDS] = Shards;
93− routes[ROUTE_NAMES.LEDGER] = Ledger;
94− routes[ROUTE_NAMES.TREZOR] = Trezor;
95− routes[ROUTE_NAMES.PRIVATE_KEY] = PrivateKey;
96− routes[ROUTE_NAMES.KEYSTORE] = Keystore;
97− routes[ROUTE_NAMES.SHIPS] = Points;
98− routes[ROUTE_NAMES.SHIP] = Point;
99− routes[ROUTE_NAMES.SET_MANAGEMENT_PROXY] = SetManagementProxy;
100− routes[ROUTE_NAMES.SET_SPAWN_PROXY] = SetSpawnProxy;
101− routes[ROUTE_NAMES.SET_TRANSFER_PROXY] = SetTransferProxy;
102− routes[ROUTE_NAMES.CREATE_GALAXY] = CreateGalaxy;
103− routes[ROUTE_NAMES.ISSUE_CHILD] = IssueChild;
104− routes[ROUTE_NAMES.SET_KEYS] = SetKeys;
105− routes[ROUTE_NAMES.TRANSFER] = Transfer;
106− routes[ROUTE_NAMES.SENT_TRANSACTION] = SentTransaction;
107− routes[ROUTE_NAMES.GEN_KEYFILE] = GenKeyfile;
108− return routes;
109−};
110−
111−const ROUTES = createRoutes();
112−
113−const renderRoute = (props, route) => {
114 const { wallet, networkType } = props;
71 const { wallet, networkType } = props;
115− return route === ROUTE_NAMES.LANDING
72+
116− ? 'Bridge'
73+ switch (route.name) {
117− : route === ROUTE_NAMES.INVITE_TICKET
74+ case ROUTE_NAMES.INVITE_TICKET:
118− ? 'Invite code'
75+ return "Invite code";
119− : route === ROUTE_NAMES.INVITE_TRANSACTIONS
76+ case ROUTE_NAMES.INVITE_TRANSACTIONS:
120− ? 'Setting up new wallet'
77+ return "Setting up new wallet";
121− : route === ROUTE_NAMES.INVITES_SEND
78+ case ROUTE_NAMES.INVITES_SEND:
122− ? 'Send invites'
79+ return "Send invites";
123− : route === ROUTE_NAMES.INVITES_MANAGE
80+ case ROUTE_NAMES.INVITES_MANAGE:
124− ? 'Manage invites'
81+ return "Manage invites";
125− : route === ROUTE_NAMES.NETWORK
82+ case ROUTE_NAMES.NETWORK:
126− ? `${renderNetworkType(networkType)}`
83+ return `${renderNetworkType(networkType)}`;
127− : route === ROUTE_NAMES.WALLET
84+ case ROUTE_NAMES.WALLET:
128− ? wallet.matchWith({
85+ return wallet.matchWith({
129− Nothing: () => 'Wallet',
86+ Nothing: () => "Wallet",
130 Just: wal =>
87 Just: wal =>
131 wal.value instanceof EthereumWallet ? (
88 wal.value instanceof EthereumWallet ? (
132 <span className="text-mono">{wal.value.address}</span>
89 <span className="text-mono">{wal.value.address}</span>
133 ) : (
90 ) : (
134 <span className="text-mono">{wal.value.address}</span>
91 <span className="text-mono">{wal.value.address}</span>
135 ),
92 ),
136− })
93+ });
137− : route === ROUTE_NAMES.MNEMONIC
94+ case ROUTE_NAMES.MNEMONIC:
138− ? 'Mnemonic'
95+ return "Mnemonic";
139− : route === ROUTE_NAMES.TICKET
96+ case ROUTE_NAMES.TICKET:
140− ? 'Urbit Ticket'
97+ return "Urbit Ticket";
141− : route === ROUTE_NAMES.SHARDS
98+ case ROUTE_NAMES.SHARDS:
142− ? 'Urbit Ticket'
99+ return "Urbit Ticket";
143− : route === ROUTE_NAMES.LEDGER
100+ case ROUTE_NAMES.LEDGER:
144− ? 'Ledger'
101+ return "Ledger";
145− : route === ROUTE_NAMES.TREZOR
102+ case ROUTE_NAMES.TREZOR:
146− ? 'Trezor'
103+ return "Trezor";
147− : route === ROUTE_NAMES.PRIVATE_KEY
104+ case ROUTE_NAMES.PRIVATE_KEY:
148− ? 'Private Key'
105+ return "Private Key";
149− : route === ROUTE_NAMES.KEYSTORE
106+ case ROUTE_NAMES.KEYSTORE:
150− ? 'Keystore File'
107+ return "Keystore File";
151− : route === ROUTE_NAMES.SHIPS
108+ case ROUTE_NAMES.SHIPS:
152− ? 'Points'
109+ return "Points";
153− : route === ROUTE_NAMES.VIEW_SHIP
110+ case ROUTE_NAMES.VIEW_SHIP:
154− ? 'View'
111+ return "View";
155− : route === ROUTE_NAMES.SHIP
112+ case ROUTE_NAMES.SHIP:
156− ? ob.patp(need.pointCursor(props))
113+ return ob.patp(need.pointCursor(props));
157− : route === ROUTE_NAMES.SET_TRANSFER_PROXY ||
114+ case ROUTE_NAMES.SET_TRANSFER_PROXY:
158− route === ROUTE_NAMES.SET_MANAGEMENT_PROXY ||
115+ case ROUTE_NAMES.SET_MANAGEMENT_PROXY:
159− route === ROUTE_NAMES.SET_SPAWN_PROXY
116+ case ROUTE_NAMES.SET_SPAWN_PROXY:
160− ? 'Set Proxy'
117+ return "Set Proxy";
161− : route === ROUTE_NAMES.CREATE_GALAXY
118+ case ROUTE_NAMES.CREATE_GALAXY:
162− ? 'Create Galaxy'
119+ return "Create Galaxy";
163− : route === ROUTE_NAMES.ISSUE_CHILD
120+ case ROUTE_NAMES.ISSUE_CHILD:
164− ? 'Issue Child'
121+ return "Issue Child";
165− : route === ROUTE_NAMES.TRANSFER
122+ case ROUTE_NAMES.TRANSFER:
166− ? 'Transfer'
123+ return "Transfer";
167− : route === ROUTE_NAMES.ACCEPT_TRANSFER
124+ case ROUTE_NAMES.ACCEPT_TRANSFER:
168− ? 'Accept Transfer'
125+ return "Accept Transfer";
169− : route === ROUTE_NAMES.CANCEL_TRANSFER
126+ case ROUTE_NAMES.CANCEL_TRANSFER:
170− ? 'Cancel Transfer'
127+ return "Cancel Transfer";
171− : route === ROUTE_NAMES.GEN_KEYFILE
128+ case ROUTE_NAMES.GEN_KEYFILE:
172− ? 'Keyfile'
129+ return "Keyfile";
173− : route === ROUTE_NAMES.SET_KEYS
130+ case ROUTE_NAMES.SET_KEYS:
174− ? 'Configure Keys'
131+ return "Configure Keys";
175− : route === ROUTE_NAMES.SENT_TRANSACTION
132+ case ROUTE_NAMES.SENT_TRANSACTION:
176− ? 'Txn'
133+ return "Txn";
134+ case ROUTE_NAMES.LANDING:
135+ default:
177− : 'Bridge';
136+ return "Bridge";
137+ }
178};
138};
179
139
180−const router = route => ROUTES[route];
140+export const router = route => ROUTES[route.name];
181−
182−export { ROUTE_NAMES, router, renderRoute };
src/bridge/lib/wallet.js+38−38
@@ -1,85 +1,85 @@
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 "urbit-key-generation/dist/index";
9−import { isAddress } from 'web3-utils';
9+import { isAddress } from "web3-utils";
10
10
11const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
11const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
12
12
13−const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
13+const ETH_ZERO_ADDR = "0x0000000000000000000000000000000000000000";
14const CURVE_ZERO_ADDR =
14const CURVE_ZERO_ADDR =
15− '0x0000000000000000000000000000000000000000000000000000000000000000';
15+ "0x0000000000000000000000000000000000000000000000000000000000000000";
16
16
17const WALLET_NAMES = {
17const WALLET_NAMES = {
18− MNEMONIC: Symbol('MNEMONIC'),
18+ MNEMONIC: Symbol("MNEMONIC"),
19− TICKET: Symbol('TICKET'),
19+ TICKET: Symbol("TICKET"),
20− SHARDS: Symbol('SHARDS'),
20+ SHARDS: Symbol("SHARDS"),
21− LEDGER: Symbol('LEDGER'),
21+ LEDGER: Symbol("LEDGER"),
22− TREZOR: Symbol('TREZOR'),
22+ TREZOR: Symbol("TREZOR"),
23− PRIVATE_KEY: Symbol('PRIVATE_KEY'),
23+ PRIVATE_KEY: Symbol("PRIVATE_KEY"),
24− KEYSTORE: Symbol('KEYSTORE'),
24+ KEYSTORE: Symbol("KEYSTORE"),
25};
25};
26
26
27function EthereumWallet(privateKey) {
27function EthereumWallet(privateKey) {
28 this.privateKey = privateKey;
28 this.privateKey = privateKey;
29 this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
29 this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
30− const pub = this.publicKey.toString('hex');
30+ const pub = this.publicKey.toString("hex");
31 this.address = addressFromSecp256k1Public(pub);
31 this.address = addressFromSecp256k1Public(pub);
32}
32}
33
33
34const renderWalletType = wallet =>
34const renderWalletType = wallet =>
35 wallet === WALLET_NAMES.MNEMONIC
35 wallet === WALLET_NAMES.MNEMONIC
36− ? 'Mnemonic'
36+ ? "Mnemonic"
37 : wallet === WALLET_NAMES.TICKET
37 : wallet === WALLET_NAMES.TICKET
38− ? 'Ticket'
38+ ? "Ticket"
39 : wallet === WALLET_NAMES.SHARDS
39 : wallet === WALLET_NAMES.SHARDS
40− ? 'Ticket Shards'
40+ ? "Ticket Shards"
41 : wallet === WALLET_NAMES.LEDGER
41 : wallet === WALLET_NAMES.LEDGER
42− ? 'Ledger'
42+ ? "Ledger"
43 : wallet === WALLET_NAMES.TREZOR
43 : wallet === WALLET_NAMES.TREZOR
44− ? 'Trezor'
44+ ? "Trezor"
45 : wallet === WALLET_NAMES.PRIVATE_KEY
45 : wallet === WALLET_NAMES.PRIVATE_KEY
46− ? 'Private Key'
46+ ? "Private Key"
47 : wallet === WALLET_NAMES.KEYSTORE
47 : wallet === WALLET_NAMES.KEYSTORE
48− ? 'Keystore File'
48+ ? "Keystore File"
49− : 'Wallet';
49+ : "Wallet";
50
50
51const addressFromSecp256k1Public = pub => {
51const addressFromSecp256k1Public = pub => {
52 const compressed = false;
52 const compressed = false;
53 const uncompressed = secp256k1.publicKeyConvert(
53 const uncompressed = secp256k1.publicKeyConvert(
54− Buffer.from(pub, 'hex'),
54+ Buffer.from(pub, "hex"),
55− compressed
55+ compressed,
56 );
56 );
57 const chopped = uncompressed.slice(1); // chop parity byte
57 const chopped = uncompressed.slice(1); // chop parity byte
58 const hashed = keccak256(chopped);
58 const hashed = keccak256(chopped);
59− const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
59+ const addr = addHexPrefix(hashed.slice(-20).toString("hex"));
60 return toChecksumAddress(addr);
60 return toChecksumAddress(addr);
61};
61};
62
62
63−const addHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex : '0x' + hex);
63+const addHexPrefix = hex => (hex.slice(0, 2) === "0x" ? hex : "0x" + hex);
64
64
65−const stripHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex.slice(2) : hex);
65+const stripHexPrefix = hex => (hex.slice(0, 2) === "0x" ? hex.slice(2) : hex);
66
66
67const keccak256 = str =>
67const keccak256 = str =>
68− keccak('keccak256')
68+ keccak("keccak256")
69 .update(str)
69 .update(str)
70 .digest();
70 .digest();
71
71
72−const isValidAddress = a => '0x0' === a || isAddress(a);
72+const isValidAddress = a => "0x0" === a || isAddress(a);
73
73
74const toChecksumAddress = address => {
74const toChecksumAddress = address => {
75 const addr = stripHexPrefix(address).toLowerCase();
75 const addr = stripHexPrefix(address).toLowerCase();
76− const hash = keccak256(addr).toString('hex');
76+ const hash = keccak256(addr).toString("hex");
77
77
78 return lodash.reduce(
78 return lodash.reduce(
79 addr,
79 addr,
80 (acc, char, idx) =>
80 (acc, char, idx) =>
81 parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
81 parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
82− '0x'
82+ "0x",
83 );
83 );
84};
84};
85
85
@@ -87,7 +87,7 @@ const eqAddr = (addr0, addr1) =>
87 toChecksumAddress(addr0) === toChecksumAddress(addr1);
87 toChecksumAddress(addr0) === toChecksumAddress(addr1);
88
88
89const urbitWalletFromTicket = async (ticket, point, passphrase) => {
89const urbitWalletFromTicket = async (ticket, point, passphrase) => {
90− if (typeof point === 'string') {
90+ if (typeof point === "string") {
91 //TODO why not in kg?
91 //TODO why not in kg?
92 point = ob.patp2dec(point);
92 point = ob.patp2dec(point);
93 }
93 }
@@ -100,7 +100,7 @@ const urbitWalletFromTicket = async (ticket, point, passphrase) => {
100};
100};
101
101
102const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
102const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
103− if (typeof point === 'string') {
103+ if (typeof point === "string") {
104 //TODO why not in kg?
104 //TODO why not in kg?
105 point = ob.patp2dec(point);
105 point = ob.patp2dec(point);
106 }
106 }
src/bridge/store/txnConfirmations.jsnew file+37
@@ -0,0 +1,37 @@
1+import React, { useContext, useState } from "react";
2+
3+export const TxnConfirmationsContext = React.createContext(null);
4+
5+export function TxnConfirmationsProvider({ children }) {
6+ const [confirmations, setConfirmations] = useState({});
7+
8+ const setTxnConfirmations = (txnHash, txnConfirmations) => {
9+ setConfirmations({
10+ ...confirmations,
11+ [txnHash]: txnConfirmations,
12+ });
13+ };
14+
15+ return (
16+ <TxnConfirmationsContext.Provider
17+ value={{
18+ txnConfirmations: confirmations,
19+ setTxnConfirmations,
20+ }}
21+ >
22+ {children}
23+ </TxnConfirmationsContext.Provider>
24+ );
25+}
26+
27+// HOC version
28+export const withTxnConfirmations = Component => props => (
29+ <TxnConfirmationsContext.Consumer>
30+ {txnConfirmations => <Component {...txnConfirmations} {...props} />}
31+ </TxnConfirmationsContext.Consumer>
32+);
33+
34+// Hook version
35+export function useTxnConfirmations() {
36+ return useContext(TxnConfirmationsContext);
37+}
src/bridge/views/AcceptTransfer.js+25−23
@@ -1,19 +1,19 @@
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";
4import {
4import {
5 InnerLabel,
5 InnerLabel,
6 AddressInput,
6 AddressInput,
7 ShowBlockie,
7 ShowBlockie,
8 Anchor,
8 Anchor,
9−} from '../components/Base';
9+} from "../components/Base";
10−import * as azimuth from 'azimuth-js';
10+import * as azimuth from "azimuth-js";
11−import * as ob from 'urbit-ob';
11+import * as ob from "urbit-ob";
12−import * as need from '../lib/need';
12+import * as need from "../lib/need";
13
13
14−import StatelessTransaction from '../components/StatelessTransaction';
14+import StatelessTransaction from "../components/StatelessTransaction";
15−import { NETWORK_NAMES } from '../lib/network';
15+import { NETWORK_NAMES } from "../lib/network";
16−import { isValidAddress } from '../lib/wallet';
16+import { isValidAddress } from "../lib/wallet";
17
17
18class AcceptTransfer extends React.Component {
18class AcceptTransfer extends React.Component {
19 constructor(props) {
19 constructor(props) {
@@ -50,8 +50,8 @@ class AcceptTransfer extends React.Component {
50 validContracts,
50 validContracts,
51 validPoint,
51 validPoint,
52 state.receivingAddress,
52 state.receivingAddress,
53− true
53+ true,
54− )
54+ ),
55 );
55 );
56 }
56 }
57
57
@@ -66,20 +66,20 @@ class AcceptTransfer extends React.Component {
66
66
67 const esdomain =
67 const esdomain =
68 props.networkType === NETWORK_NAMES.ROPSTEN
68 props.networkType === NETWORK_NAMES.ROPSTEN
69− ? 'ropsten.etherscan.io'
69+ ? "ropsten.etherscan.io"
70− : 'etherscan.io';
70+ : "etherscan.io";
71
71
72 return (
72 return (
73 <Row>
73 <Row>
74 <Col>
74 <Col>
75 <H1>
75 <H1>
76− {'Accept Transfer of '}{' '}
76+ {"Accept Transfer of "}{" "}
77 <code>{` ${ob.patp(state.incomingPoint)} `}</code>
77 <code>{` ${ob.patp(state.incomingPoint)} `}</code>
78 </H1>
78 </H1>
79
79
80 <P>
80 <P>
81 {"By default, the recipient is the address you're logged in " +
81 {"By default, the recipient is the address you're logged in " +
82− 'as. But you can transfer to any address you like.'}
82+ "as. But you can transfer to any address you like."}
83 </P>
83 </P>
84
84
85 <AddressInput
85 <AddressInput
@@ -87,20 +87,22 @@ class AcceptTransfer extends React.Component {
87 prop-size="lg"
87 prop-size="lg"
88 prop-format="innerLabel"
88 prop-format="innerLabel"
89 value={state.receivingAddress}
89 value={state.receivingAddress}
90− onChange={v => this.handleAddressInput(v)}>
90+ onChange={v => this.handleAddressInput(v)}
91+ >
91− <InnerLabel>{'Receiving Address'}</InnerLabel>
92+ <InnerLabel>{"Receiving Address"}</InnerLabel>
92− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
93+ <ShowBlockie className={"mt-1"} address={state.receivingAddress} />
93 </AddressInput>
94 </AddressInput>
94
95
95 <Anchor
96 <Anchor
96− className={'mt-1 sm'}
97+ className={"mt-1 sm"}
97 prop-size="sm"
98 prop-size="sm"
98 prop-disabled={
99 prop-disabled={
99 !isValidAddress(state.receivingAddress) || !esvisible
100 !isValidAddress(state.receivingAddress) || !esvisible
100 }
101 }
101− target={'_blank'}
102+ target={"_blank"}
102− href={`https://${esdomain}/address/${state.receivingAddress}`}>
103+ href={`https://${esdomain}/address/${state.receivingAddress}`}
104+ >
103− {'View on Etherscan ↗'}
105+ {"View on Etherscan ↗"}
104 </Anchor>
106 </Anchor>
105
107
106 <StatelessTransaction
108 <StatelessTransaction
src/bridge/views/CancelTransfer.js+12−12
@@ -1,12 +1,12 @@
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) {
@@ -15,7 +15,7 @@ class CancelTransfer extends React.Component {
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
@@ -32,7 +32,7 @@ class CancelTransfer extends React.Component {
32 const txn = azimuth.ecliptic.setTransferProxy(
32 const txn = azimuth.ecliptic.setTransferProxy(
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);
@@ -45,7 +45,7 @@ class CancelTransfer extends React.Component {
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
@@ -55,7 +55,7 @@ class CancelTransfer extends React.Component {
55 <Row>
55 <Row>
56 <Col>
56 <Col>
57 <H1>
57 <H1>
58− {'Cancel Transfer of '}{' '}
58+ {"Cancel Transfer of "}{" "}
59 <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
59 <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
60 </H1>
60 </H1>
61
61
src/bridge/views/CreateGalaxy.js+43−39
@@ -1,37 +1,37 @@
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";
9import {
9import {
10 InnerLabel,
10 InnerLabel,
11 GalaxyInput,
11 GalaxyInput,
12 AddressInput,
12 AddressInput,
13 ValidatedSigil,
13 ValidatedSigil,
14 ShowBlockie,
14 ShowBlockie,
15−} from '../components/Base';
15+} from "../components/Base";
16−import StatelessTransaction from '../components/StatelessTransaction';
16+import StatelessTransaction from "../components/StatelessTransaction";
17
17
18−import { NETWORK_NAMES } from '../lib/network';
18+import { NETWORK_NAMES } from "../lib/network";
19−import { canDecodePatp } from '../lib/txn';
19+import { canDecodePatp } from "../lib/txn";
20
20
21−import { ETH_ZERO_ADDR, isValidAddress, eqAddr } from '../lib/wallet';
21+import { ETH_ZERO_ADDR, isValidAddress, eqAddr } from "../lib/wallet";
22
22
23−import { isValidGalaxy } from '../lib/lib';
23+import { isValidGalaxy } from "../lib/lib";
24
24
25const buttonTriState = status => {
25const buttonTriState = status => {
26− if (status === null) return 'blue';
26+ if (status === null) return "blue";
27− if (status === false) return 'yellow';
27+ if (status === false) return "yellow";
28− if (status === true) return 'green';
28+ if (status === true) return "green";
29};
29};
30
30
31const buttonTriStateText = status => {
31const buttonTriStateText = status => {
32− if (status === null) return 'Confirm Galaxy Availablility';
32+ if (status === null) return "Confirm Galaxy Availablility";
33− if (status === false) return 'Galaxy is Not Available';
33+ if (status === false) return "Galaxy is Not Available";
34− if (status === true) return 'Galaxy is Available';
34+ if (status === true) return "Galaxy is Available";
35};
35};
36
36
37class CreateGalaxy extends React.Component {
37class CreateGalaxy extends React.Component {
@@ -42,7 +42,7 @@ class CreateGalaxy extends React.Component {
42
42
43 this.state = {
43 this.state = {
44 galaxyOwner: galaxyOwner,
44 galaxyOwner: galaxyOwner,
45− galaxyName: '',
45+ galaxyName: "",
46 isAvailable: null,
46 isAvailable: null,
47 };
47 };
48
48
@@ -77,7 +77,7 @@ class CreateGalaxy extends React.Component {
77 const txn = azimuth.ecliptic.createGalaxy(
77 const txn = azimuth.ecliptic.createGalaxy(
78 validContracts,
78 validContracts,
79 galaxyDec,
79 galaxyDec,
80− state.galaxyOwner
80+ state.galaxyOwner,
81 );
81 );
82
82
83 return Maybe.Just(txn);
83 return Maybe.Just(txn);
@@ -97,7 +97,7 @@ class CreateGalaxy extends React.Component {
97
97
98 const currentOwner = await azimuth.azimuth.getOwner(
98 const currentOwner = await azimuth.azimuth.getOwner(
99 validContracts,
99 validContracts,
100− galaxyDec
100+ galaxyDec,
101 );
101 );
102
102
103 const available = eqAddr(currentOwner, ETH_ZERO_ADDR);
103 const available = eqAddr(currentOwner, ETH_ZERO_ADDR);
@@ -122,17 +122,17 @@ class CreateGalaxy extends React.Component {
122
122
123 const esdomain =
123 const esdomain =
124 props.networkType === NETWORK_NAMES.ROPSTEN
124 props.networkType === NETWORK_NAMES.ROPSTEN
125− ? 'ropsten.etherscan.io'
125+ ? "ropsten.etherscan.io"
126− : 'etherscan.io';
126+ : "etherscan.io";
127
127
128 return (
128 return (
129 <Row>
129 <Row>
130 <Col>
130 <Col>
131− <H1> {'Create a Galaxy'} </H1>
131+ <H1> {"Create a Galaxy"} </H1>
132
132
133 <P>
133 <P>
134− {'Enter the galaxy to create and the address that will own ' +
134+ {"Enter the galaxy to create and the address that will own " +
135− 'it (defaulting to this account, if not provided).'}
135+ "it (defaulting to this account, if not provided)."}
136 </P>
136 </P>
137
137
138 <GalaxyInput
138 <GalaxyInput
@@ -142,8 +142,9 @@ class CreateGalaxy extends React.Component {
142 autoFocus
142 autoFocus
143 placeholder="e.g. ~zod"
143 placeholder="e.g. ~zod"
144 value={state.galaxyName}
144 value={state.galaxyName}
145− onChange={v => this.handleGalaxyNameInput(v)}>
145+ onChange={v => this.handleGalaxyNameInput(v)}
146+ >
146− <InnerLabel>{'Galaxy Name'}</InnerLabel>
147+ <InnerLabel>{"Galaxy Name"}</InnerLabel>
147 <ValidatedSigil
148 <ValidatedSigil
148 className="tr-0 mt-05 mr-0 abs"
149 className="tr-0 mt-05 mr-0 abs"
149 patp={state.galaxyName}
150 patp={state.galaxyName}
@@ -157,18 +158,20 @@ class CreateGalaxy extends React.Component {
157 prop-size="lg"
158 prop-size="lg"
158 prop-format="innerLabel"
159 prop-format="innerLabel"
159 value={state.galaxyOwner}
160 value={state.galaxyOwner}
160− onChange={v => this.handleAddressInput(v)}>
161+ onChange={v => this.handleAddressInput(v)}
162+ >
161− <InnerLabel>{'Address that will own this galaxy'}</InnerLabel>
163+ <InnerLabel>{"Address that will own this galaxy"}</InnerLabel>
162− <ShowBlockie className={'mt-1'} address={state.galaxyOwner} />
164+ <ShowBlockie className={"mt-1"} address={state.galaxyOwner} />
163 </AddressInput>
165 </AddressInput>
164
166
165 <Anchor
167 <Anchor
166− className={'mt-1'}
168+ className={"mt-1"}
167− prop-size={'sm'}
169+ prop-size={"sm"}
168 prop-disabled={!validAddress || !esvisible}
170 prop-disabled={!validAddress || !esvisible}
169− target={'_blank'}
171+ target={"_blank"}
170− href={`https://${esdomain}/address/${state.galaxyOwner}`}>
172+ href={`https://${esdomain}/address/${state.galaxyOwner}`}
173+ >
171− {'View on Etherscan ↗'}
174+ {"View on Etherscan ↗"}
172 </Anchor>
175 </Anchor>
173
176
174 <Button
177 <Button
@@ -176,7 +179,8 @@ class CreateGalaxy extends React.Component {
176 className="mt-8"
179 className="mt-8"
177 prop-color={buttonTriState(state.isAvailable)}
180 prop-color={buttonTriState(state.isAvailable)}
178 disabled={!validGalaxy}
181 disabled={!validGalaxy}
179− onClick={() => this.confirmAvailability()}>
182+ onClick={() => this.confirmAvailability()}
183+ >
180 {buttonTriStateText(state.isAvailable)}
184 {buttonTriStateText(state.isAvailable)}
181 </Button>
185 </Button>
182
186
src/bridge/views/GenKeyfile.js+23−22
@@ -1,23 +1,23 @@
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 "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 { addHexPrefix } from '../lib/wallet';
13+import { addHexPrefix } from "../lib/wallet";
14
14
15class GenKeyfile extends React.Component {
15class GenKeyfile extends React.Component {
16 constructor(props) {
16 constructor(props) {
17 super(props);
17 super(props);
18
18
19 this.state = {
19 this.state = {
20− keyfile: '',
20+ keyfile: "",
21 loaded: false,
21 loaded: false,
22 };
22 };
23 }
23 }
@@ -42,7 +42,7 @@ class GenKeyfile extends React.Component {
42
42
43 async componentDidMount() {
43 async componentDidMount() {
44 const { point, pointDetails, revision } = this.getPointDetails();
44 const { point, pointDetails, revision } = this.getPointDetails();
45− let keyfile = '';
45+ let keyfile = "";
46
46
47 const hexRegExp = /[0-9A-Fa-f]{64}/g;
47 const hexRegExp = /[0-9A-Fa-f]{64}/g;
48 const networkSeed = await this.deriveSeed();
48 const networkSeed = await this.deriveSeed();
@@ -77,11 +77,11 @@ class GenKeyfile extends React.Component {
77 const next = false;
77 const next = false;
78 let seed = await attemptSeedDerivation(next, this.props);
78 let seed = await attemptSeedDerivation(next, this.props);
79
79
80− if (seed.getOrElse('') === '' && this.props.networkSeedCache) {
80+ if (seed.getOrElse("") === "" && this.props.networkSeedCache) {
81 seed = Maybe.Just(this.props.networkSeedCache);
81 seed = Maybe.Just(this.props.networkSeedCache);
82 }
82 }
83
83
84− return seed.getOrElse('');
84+ return seed.getOrElse("");
85 }
85 }
86
86
87 render() {
87 render() {
@@ -90,14 +90,14 @@ class GenKeyfile extends React.Component {
90
90
91 return (
91 return (
92 <Row>
92 <Row>
93− <Col className={'col-md-8'}>
93+ <Col className={"col-md-8"}>
94− <H1>{'Generate keyfile'}</H1>
94+ <H1>{"Generate keyfile"}</H1>
95
95
96− <P>{'Download a private key file for booting this point in Arvo.'}</P>
96+ <P>{"Download a private key file for booting this point in Arvo."}</P>
97
97
98− {keyfile === '' && !loaded && <P>{'Generating keyfile...'}</P>}
98+ {keyfile === "" && !loaded && <P>{"Generating keyfile..."}</P>}
99
99
100− {keyfile === '' && loaded && (
100+ {keyfile === "" && loaded && (
101 <React.Fragment>
101 <React.Fragment>
102 <P>
102 <P>
103 <b>Warning: </b>
103 <b>Warning: </b>
@@ -112,16 +112,17 @@ class GenKeyfile extends React.Component {
112 </React.Fragment>
112 </React.Fragment>
113 )}
113 )}
114
114
115− {keyfile !== '' && (
115+ {keyfile !== "" && (
116 <React.Fragment>
116 <React.Fragment>
117 <div className="pb-5 text-code keyfile">{keyfile}</div>
117 <div className="pb-5 text-code keyfile">{keyfile}</div>
118 <Button
118 <Button
119 onClick={() => {
119 onClick={() => {
120 let blob = new Blob([keyfile], {
120 let blob = new Blob([keyfile], {
121− type: 'text/plain;charset=utf-8',
121+ type: "text/plain;charset=utf-8",
122 });
122 });
123 saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
123 saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
124− }}>
124+ }}
125+ >
125 Download →
126 Download →
126 </Button>
127 </Button>
127 </React.Fragment>
128 </React.Fragment>
src/bridge/views/InviteTicket.js+44−37
@@ -1,5 +1,5 @@
1−import { Just, Nothing } from 'folktale/maybe';
1+import { Just, Nothing } from "folktale/maybe";
2−import React from 'react';
2+import React from "react";
3import {
3import {
4 InnerLabel,
4 InnerLabel,
5 Warning,
5 Warning,
@@ -9,34 +9,35 @@ import {
9 Row,
9 Row,
10 Col,
10 Col,
11 Passport,
11 Passport,
12−} from '../components/Base';
12+} from "../components/Base";
13−import * as azimuth from 'azimuth-js';
13+import * as azimuth from "azimuth-js";
14−import * as need from '../lib/need';
14+import * as need from "../lib/need";
15
15
16−import { randomPatq } from '../lib/lib';
16+import { randomPatq } from "../lib/lib";
17−import { ROUTE_NAMES } from '../lib/router';
17+import { ROUTE_NAMES } from "../lib/routeNames";
18+import { withHistory } from "../lib/history";
18import {
19import {
19 DEFAULT_HD_PATH,
20 DEFAULT_HD_PATH,
20 ownershipWalletFromTicket,
21 ownershipWalletFromTicket,
21 walletFromMnemonic,
22 walletFromMnemonic,
22−} from '../lib/wallet';
23+} from "../lib/wallet";
23−import { BRIDGE_ERROR } from '../lib/error';
24+import { BRIDGE_ERROR } from "../lib/error";
24import {
25import {
25 INVITE_STAGES,
26 INVITE_STAGES,
26 WALLET_STATES,
27 WALLET_STATES,
27 TRANSACTION_STATES,
28 TRANSACTION_STATES,
28−} from '../lib/invite';
29+} from "../lib/invite";
29−import { generateWallet, startTransactions } from '../lib/invite';
30+import { generateWallet, startTransactions } from "../lib/invite";
30
31
31class InviteTicket extends React.Component {
32class InviteTicket extends React.Component {
32 constructor(props) {
33 constructor(props) {
33 super(props);
34 super(props);
34
35
35 this.state = {
36 this.state = {
36− inviteTicket: '',
37+ inviteTicket: "",
37 inviteWallet: Nothing(),
38 inviteWallet: Nothing(),
38− verifyTicket: '',
39+ verifyTicket: "",
39− realTicket: '',
40+ realTicket: "",
40 realPoint: Nothing(),
41 realPoint: Nothing(),
41 realWallet: Nothing(),
42 realWallet: Nothing(),
42 walletReady: false,
43 walletReady: false,
@@ -63,8 +64,8 @@ class InviteTicket extends React.Component {
63 }
64 }
64
65
65 navigateLogin() {
66 navigateLogin() {
66− this.props.popRoute();
67+ this.props.history.pop();
67− this.props.pushRoute(ROUTE_NAMES.NETWORK);
68+ this.props.history.push(ROUTE_NAMES.NETWORK);
68 }
69 }
69
70
70 handleInviteTicketInput(inviteTicket) {
71 handleInviteTicketInput(inviteTicket) {
@@ -91,7 +92,7 @@ class InviteTicket extends React.Component {
91 //TODO isn't all this accessible in the ownership object?
92 //TODO isn't all this accessible in the ownership object?
92 const inviteWallet = walletFromMnemonic(
93 const inviteWallet = walletFromMnemonic(
93 mnemonic,
94 mnemonic,
94− DEFAULT_HD_PATH
95+ DEFAULT_HD_PATH,
95 ).matchWith({
96 ).matchWith({
96 Just: w => w.value,
97 Just: w => w.value,
97 Nothing: () => {
98 Nothing: () => {
@@ -129,7 +130,7 @@ class InviteTicket extends React.Component {
129 }
130 }
130 } else {
131 } else {
131 errors = [
132 errors = [
132− 'Invite code has no claimable ship. Check your invite code and try again.',
133+ "Invite code has no claimable ship. Check your invite code and try again.",
133 ];
134 ];
134 }
135 }
135
136
@@ -139,7 +140,7 @@ class InviteTicket extends React.Component {
139 // dev mode: uncomment this out to autofill verified ticket
140 // dev mode: uncomment this out to autofill verified ticket
140 verifyTicket: !Nothing.hasInstance(realWallet)
141 verifyTicket: !Nothing.hasInstance(realWallet)
141 ? realWallet.value.ticket
142 ? realWallet.value.ticket
142− : '',
143+ : "",
143 walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING),
144 walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING),
144 errors,
145 errors,
145 });
146 });
@@ -156,11 +157,11 @@ class InviteTicket extends React.Component {
156 let handler = isLogin
157 let handler = isLogin
157 ? this.handleInviteTicketInput
158 ? this.handleInviteTicketInput
158 : this.handleVerifyTicketInput;
159 : this.handleVerifyTicketInput;
159− let label = isLogin ? 'Activation code' : 'ticket';
160+ let label = isLogin ? "Activation code" : "ticket";
160
161
161 // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing
162 // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing
162 const Verified = this.state.realWallet.map(realWallet =>
163 const Verified = this.state.realWallet.map(realWallet =>
163− VerifyTicketInput(realWallet.ticket)
164+ VerifyTicketInput(realWallet.ticket),
164 );
165 );
165
166
166 // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing
167 // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing
@@ -178,7 +179,8 @@ class InviteTicket extends React.Component {
178 name="invite"
179 name="invite"
179 placeholder={`e.g. ${phTick}`}
180 placeholder={`e.g. ${phTick}`}
180 value={value}
181 value={value}
181− onChange={handler}>
182+ onChange={handler}
183+ >
182 <InnerLabel>{label}</InnerLabel>
184 <InnerLabel>{label}</InnerLabel>
183 </TicketElem>
185 </TicketElem>
184 );
186 );
@@ -254,7 +256,7 @@ class InviteTicket extends React.Component {
254
256
255 updateProgress(notification) {
257 updateProgress(notification) {
256 let newState =
258 let newState =
257− notification.type === 'progress'
259+ notification.type === "progress"
258 ? { transactionProgress: notification.value }
260 ? { transactionProgress: notification.value }
259 : notification.type === 'notify'
261 : notification.type === 'notify'
260 ? { errors: [notification.value] }
262 ? { errors: [notification.value] }
@@ -276,8 +278,8 @@ class InviteTicket extends React.Component {
276 const { web3, contracts, setUrbitWallet } = this.props;
278 const { web3, contracts, setUrbitWallet } = this.props;
277
279
278 const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY)
280 const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY)
279− ? 'green'
281+ ? "green"
280− : 'black';
282+ : "black";
281
283
282 const clickHandler =
284 const clickHandler =
283 stage === INVITE_STAGES.INVITE_LOGIN
285 stage === INVITE_STAGES.INVITE_LOGIN
@@ -295,7 +297,7 @@ class InviteTicket extends React.Component {
295 this.setState({
297 this.setState({
296 stage: INVITE_STAGES.INVITE_TRANSACTIONS,
298 stage: INVITE_STAGES.INVITE_TRANSACTIONS,
297 walletStates: this.state.walletStates.concat(
299 walletStates: this.state.walletStates.concat(
298− WALLET_STATES.TRANSACTIONS
300+ WALLET_STATES.TRANSACTIONS,
299 ),
301 ),
300 });
302 });
301
303
@@ -310,7 +312,7 @@ class InviteTicket extends React.Component {
310 updateProgress: this.updateProgress,
312 updateProgress: this.updateProgress,
311 }).then(() => {
313 }).then(() => {
312 setTimeout(() => {
314 setTimeout(() => {
313− this.props.popRoute();
315+ this.props.history.pop();
314 }, 5000);
316 }, 5000);
315 });
317 });
316 }
318 }
@@ -331,12 +333,13 @@ class InviteTicket extends React.Component {
331 const buttonElem =
333 const buttonElem =
332 stage === INVITE_STAGES.INVITE_TRANSACTIONS ? null : (
334 stage === INVITE_STAGES.INVITE_TRANSACTIONS ? null : (
333 <Button
335 <Button
334− className={'mt-4'}
336+ className={"mt-4"}
335− prop-size={'lg'}
337+ prop-size={"lg"}
336 prop-color={btnColor}
338 prop-color={btnColor}
337 disabled={!continueReady}
339 disabled={!continueReady}
338− onClick={clickHandler}>
340+ onClick={clickHandler}
341+ >
339− {'Continue →'}
342+ {"Continue →"}
340 </Button>
343 </Button>
341 );
344 );
342
345
@@ -352,17 +355,20 @@ class InviteTicket extends React.Component {
352 <div className="mt-6 mb-8">
355 <div className="mt-6 mb-8">
353 <span
356 <span
354 className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET &&
357 className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET &&
355− 'gray-20'}`}>
358+ "gray-20"}`}
359+ >
356 1 Passport
360 1 Passport
357 </span>
361 </span>
358 <span
362 <span
359 className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY &&
363 className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY &&
360− 'gray-20'}`}>
364+ "gray-20"}`}
365+ >
361 2 Verify
366 2 Verify
362 </span>
367 </span>
363 <span
368 <span
364 className={`fs-35 mr-3 ${stage !==
369 className={`fs-35 mr-3 ${stage !==
365− INVITE_STAGES.INVITE_TRANSACTIONS && 'gray-20'}`}>
370+ INVITE_STAGES.INVITE_TRANSACTIONS && "gray-20"}`}
371+ >
366 3 Invite
372 3 Invite
367 </span>
373 </span>
368 </div>
374 </div>
@@ -376,7 +382,7 @@ class InviteTicket extends React.Component {
376
382
377 return (
383 return (
378 <Warning>
384 <Warning>
379− <h3 className={'mb-2'}>{'Warning'}</h3>
385+ <h3 className={"mb-2"}>{"Warning"}</h3>
380 {errorElems}
386 {errorElems}
381 </Warning>
387 </Warning>
382 );
388 );
@@ -403,7 +409,8 @@ class InviteTicket extends React.Component {
403 prop-type={`link`}
409 prop-type={`link`}
404 prop-size={`sm`}
410 prop-size={`sm`}
405 className={`block mt-4`}
411 className={`block mt-4`}
406− onClick={this.navigateLogin}>
412+ onClick={this.navigateLogin}
413+ >
407 {`Log in`}
414 {`Log in`}
408 </Button>
415 </Button>
409 )}
416 )}
@@ -421,4 +428,4 @@ class InviteTicket extends React.Component {
421 }
428 }
422}
429}
423
430
424−export default InviteTicket;
431+export default withHistory(InviteTicket);
src/bridge/views/InvitesManage.js+24−22
@@ -1,8 +1,8 @@
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
7import {
7import {
8 Row,
8 Row,
@@ -12,8 +12,8 @@ import {
12 PointInput,
12 PointInput,
13 InnerLabel,
13 InnerLabel,
14 ValidatedSigil,
14 ValidatedSigil,
15−} from '../components/Base';
15+} from "../components/Base";
16−import StatelessTransaction from '../components/StatelessTransaction';
16+import StatelessTransaction from "../components/StatelessTransaction";
17
17
18class InvitesManage extends React.Component {
18class InvitesManage extends React.Component {
19 constructor(props) {
19 constructor(props) {
@@ -21,7 +21,7 @@ class InvitesManage extends React.Component {
21
21
22 this.state = {
22 this.state = {
23 invitesWillWork: Nothing(),
23 invitesWillWork: Nothing(),
24− planetInput: '',
24+ planetInput: "",
25 targetPlanet: Nothing(),
25 targetPlanet: Nothing(),
26 currentPoolSize: Nothing(),
26 currentPoolSize: Nothing(),
27 cachedPoolSizes: {},
27 cachedPoolSizes: {},
@@ -44,7 +44,7 @@ class InvitesManage extends React.Component {
44 .isSpawnProxy(
44 .isSpawnProxy(
45 this.contracts,
45 this.contracts,
46 this.point,
46 this.point,
47− this.contracts.delegatedSending.address
47+ this.contracts.delegatedSending.address,
48 )
48 )
49 .then(isSpawnProxy => {
49 .then(isSpawnProxy => {
50 this.setState({ invitesWillWork: Just(isSpawnProxy) });
50 this.setState({ invitesWillWork: Just(isSpawnProxy) });
@@ -77,7 +77,7 @@ class InvitesManage extends React.Component {
77
77
78 checkCurrentInvites(who) {
78 checkCurrentInvites(who) {
79 const cachedPoolSizes = this.state.cachedPoolSizes;
79 const cachedPoolSizes = this.state.cachedPoolSizes;
80− if (typeof cachedPoolSizes[who] !== 'undefined') {
80+ if (typeof cachedPoolSizes[who] !== "undefined") {
81 return Just(cachedPoolSizes[who]);
81 return Just(cachedPoolSizes[who]);
82 } else {
82 } else {
83 azimuth.delegatedSending.getPool(this.contracts, who).then(pool => {
83 azimuth.delegatedSending.getPool(this.contracts, who).then(pool => {
@@ -112,8 +112,8 @@ class InvitesManage extends React.Component {
112 this.contracts,
112 this.contracts,
113 this.point,
113 this.point,
114 this.state.targetPlanet.value,
114 this.state.targetPlanet.value,
115− this.state.targetPoolSize
115+ this.state.targetPoolSize,
116− )
116+ ),
117 );
117 );
118 }
118 }
119
119
@@ -122,18 +122,18 @@ class InvitesManage extends React.Component {
122 if (this.state.invitesWillWork.value === false) {
122 if (this.state.invitesWillWork.value === false) {
123 spawnProxyWarning = (
123 spawnProxyWarning = (
124 <Warning>
124 <Warning>
125− <h3 className={'mb-2'}>{'Warning'}</h3>
125+ <h3 className={"mb-2"}>{"Warning"}</h3>
126− {'Planets under this star will not be able to send invites ' +
126+ {"Planets under this star will not be able to send invites " +
127− 'until the invite contract ('}
127+ "until the invite contract ("}
128 <code>{this.contracts.delegatedSending.address}</code>
128 <code>{this.contracts.delegatedSending.address}</code>
129− {') is made spawn proxy for this star.'}
129+ {") is made spawn proxy for this star."}
130 </Warning>
130 </Warning>
131 );
131 );
132 }
132 }
133
133
134 let poolSizeText = this.state.currentPoolSize.matchWith({
134 let poolSizeText = this.state.currentPoolSize.matchWith({
135 Just: ps => `(currently ${ps.value})`,
135 Just: ps => `(currently ${ps.value})`,
136− Nothing: _ => '',
136+ Nothing: _ => "",
137 });
137 });
138
138
139 return (
139 return (
@@ -141,7 +141,7 @@ class InvitesManage extends React.Component {
141 <Col>
141 <Col>
142 <p>
142 <p>
143 {
143 {
144− 'manage invites here, for stars. can only give invites to child planets'
144+ "manage invites here, for stars. can only give invites to child planets"
145 }
145 }
146 </p>
146 </p>
147
147
@@ -150,10 +150,11 @@ class InvitesManage extends React.Component {
150 <PointInput
150 <PointInput
151 prop-format="innerlabel"
151 prop-format="innerlabel"
152 prop-size="lg"
152 prop-size="lg"
153− placeholder={'~sampel-sipnem'}
153+ placeholder={"~sampel-sipnem"}
154 value={this.state.planetInput}
154 value={this.state.planetInput}
155− onChange={this.handlePointInput}>
155+ onChange={this.handlePointInput}
156+ >
156− <InnerLabel>{'Planet to set invites for'}</InnerLabel>
157+ <InnerLabel>{"Planet to set invites for"}</InnerLabel>
157 <ValidatedSigil
158 <ValidatedSigil
158 patp={this.state.targetPlanet}
159 patp={this.state.targetPlanet}
159 validator={() => this.validatePoint}
160 validator={() => this.validatePoint}
@@ -164,7 +165,8 @@ class InvitesManage extends React.Component {
164 prop-format="innerlabel"
165 prop-format="innerlabel"
165 prop-size="lg"
166 prop-size="lg"
166 value={this.state.targetPoolSize}
167 value={this.state.targetPoolSize}
167− onChange={this.handlePoolSizeInput}>
168+ onChange={this.handlePoolSizeInput}
169+ >
168 <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
170 <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
169 </Input>
171 </Input>
170
172
src/bridge/views/InvitesSend.js+73−68
@@ -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 {
@@ -17,27 +17,27 @@ import {
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;
25−const INVITE_COST = toWei((GAS_PRICE_GWEI * GAS_LIMIT).toString(), 'gwei');
25+const INVITE_COST = toWei((GAS_PRICE_GWEI * GAS_LIMIT).toString(), "gwei");
26
26
27const STATUS = {
27const STATUS = {
28− INPUT: 'Generate invites',
28+ INPUT: "Generate invites",
29− GENERATING: 'Generating invites',
29+ GENERATING: "Generating invites",
30− CAN_SEND: 'Send invites',
30+ CAN_SEND: "Send invites",
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 {
@@ -47,7 +47,7 @@ class InvitesSend extends React.Component {
47 this.state = {
47 this.state = {
48 invitesAvailable: Nothing(),
48 invitesAvailable: Nothing(),
49 invited: Nothing(),
49 invited: Nothing(),
50− targets: [{ email: '', hasReceived: Nothing(), status: Nothing() }],
50+ targets: [{ email: "", hasReceived: Nothing(), status: Nothing() }],
51 status: STATUS.INPUT,
51 status: STATUS.INPUT,
52 errors: Nothing(),
52 errors: Nothing(),
53 //
53 //
@@ -89,7 +89,7 @@ class InvitesSend extends React.Component {
89 async findInvited() {
89 async findInvited() {
90 let invited = await azimuth.delegatedSending.getInvited(
90 let invited = await azimuth.delegatedSending.getInvited(
91 this.contracts,
91 this.contracts,
92− this.point
92+ this.point,
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);
@@ -116,13 +116,13 @@ class InvitesSend extends React.Component {
116 const planets = await azimuth.delegatedSending.getPlanetsToSend(
116 const planets = await azimuth.delegatedSending.getPlanetsToSend(
117 this.contracts,
117 this.contracts,
118 this.point,
118 this.point,
119− targets.length
119+ targets.length,
120 );
120 );
121
121
122 // 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
123 // composing our target list
123 // composing our target list
124 if (planets.length < targets.length) {
124 if (planets.length < targets.length) {
125− this.addError('Can currently only send ' + planets.length + ' invites!');
125+ this.addError("Can currently only send " + planets.length + " invites!");
126 return;
126 return;
127 }
127 }
128
128
@@ -132,7 +132,7 @@ class InvitesSend extends React.Component {
132 // create invite wallets and transactions
132 // create invite wallets and transactions
133 let invites = []; // { email, ticket, signedTx, rawTx }
133 let invites = []; // { email, ticket, signedTx, rawTx }
134 for (let i = 0; i < targets.length; i++) {
134 for (let i = 0; i < targets.length; i++) {
135− console.log('generating email', i, targets[i].email);
135+ console.log("generating email", i, targets[i].email);
136 this.setEmailStatus(i, EMAIL_STATUS.BUSY);
136 this.setEmailStatus(i, EMAIL_STATUS.BUSY);
137
137
138 const email = targets[i].email;
138 const email = targets[i].email;
@@ -142,7 +142,7 @@ class InvitesSend extends React.Component {
142 this.contracts,
142 this.contracts,
143 this.point,
143 this.point,
144 planets[i],
144 planets[i],
145− wallet.owner
145+ wallet.owner,
146 );
146 );
147 const signedTx = await signTransaction({
147 const signedTx = await signTransaction({
148 ...this.props,
148 ...this.props,
@@ -183,7 +183,7 @@ class InvitesSend extends React.Component {
183 this.askForFunding,
183 this.askForFunding,
184 () => {
184 () => {
185 this.setState({ fundingMessage: Nothing() });
185 this.setState({ fundingMessage: Nothing() });
186− }
186+ },
187 );
187 );
188
188
189 this.setState({ status: STATUS.SENDING });
189 this.setState({ status: STATUS.SENDING });
@@ -200,11 +200,11 @@ class InvitesSend extends React.Component {
200 web3,
200 web3,
201 Just(invite.signedTx),
201 Just(invite.signedTx),
202 tankWasUsed,
202 tankWasUsed,
203− () => {}
203+ () => {},
204 );
204 );
205 } catch (err) {
205 } catch (err) {
206− console.error('invite tx sending failed', err);
206+ console.error("invite tx sending failed", err);
207− this.addError('Invite transaction not sent for ' + invite.email);
207+ this.addError("Invite transaction not sent for " + invite.email);
208 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
208 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
209 continue;
209 continue;
210 }
210 }
@@ -216,19 +216,19 @@ class InvitesSend extends React.Component {
216 const mailSuccess = await sendMail(
216 const mailSuccess = await sendMail(
217 invite.email,
217 invite.email,
218 invite.ticket,
218 invite.ticket,
219− invite.rawTx
219+ invite.rawTx,
220 );
220 );
221 if (!mailSuccess) throw new Error();
221 if (!mailSuccess) throw new Error();
222 this.setEmailStatus(i, EMAIL_STATUS.DONE);
222 this.setEmailStatus(i, EMAIL_STATUS.DONE);
223 } catch (e) {
223 } catch (e) {
224− console.log('email send failed');
224+ console.log("email send failed");
225 //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
226 // that for a fact yet...
226 // that for a fact yet...
227 this.addError(
227 this.addError(
228− 'Invite email failed to send for ' +
228+ "Invite email failed to send for " +
229 invite.email +
229 invite.email +
230− '. Please give them this ticket: ' +
230+ ". Please give them this ticket: " +
231− invite.ticket
231+ invite.ticket,
232 );
232 );
233 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
233 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
234 continue;
234 continue;
@@ -240,12 +240,12 @@ class InvitesSend extends React.Component {
240 if (success) {
240 if (success) {
241 this.setEmailStatus(i, EMAIL_STATUS.DONE);
241 this.setEmailStatus(i, EMAIL_STATUS.DONE);
242 } else {
242 } else {
243− console.log('invite tx rejected');
243+ console.log("invite tx rejected");
244− this.addError('Invite transaction failed for ' + invite.email);
244+ this.addError("Invite transaction failed for " + invite.email);
245 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
245 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
246 }
246 }
247 return success;
247 return success;
248− })
248+ }),
249 );
249 );
250 }
250 }
251
251
@@ -279,9 +279,9 @@ class InvitesSend extends React.Component {
279 fundingMessage: Just(
279 fundingMessage: Just(
280 `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
280 `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
281 `we'll continue once that's true. Current balance: ${fromWei(
281 `we'll continue once that's true. Current balance: ${fromWei(
282− balance
282+ balance,
283 )}.` +
283 )}.` +
284− `Waiting...`
284+ `Waiting...`,
285 ),
285 ),
286 });
286 });
287 }
287 }
@@ -328,7 +328,7 @@ class InvitesSend extends React.Component {
328
328
329 addTarget() {
329 addTarget() {
330 let targets = this.state.targets;
330 let targets = this.state.targets;
331− targets.push({ email: '', hasReceived: Nothing(), status: Nothing() });
331+ targets.push({ email: "", hasReceived: Nothing(), status: Nothing() });
332 this.setState({ targets });
332 this.setState({ targets });
333 }
333 }
334
334
@@ -343,24 +343,24 @@ class InvitesSend extends React.Component {
343 this.state.status === STATUS.DONE ? this.state.targets.length : null;
343 this.state.status === STATUS.DONE ? this.state.targets.length : null;
344
344
345 const invitesAvailable = this.state.invitesAvailable.matchWith({
345 const invitesAvailable = this.state.invitesAvailable.matchWith({
346− Nothing: () => 'Loading...',
346+ Nothing: () => "Loading...",
347 Just: invites => {
347 Just: invites => {
348− const dia = dif ? `(-${dif})` : '';
348+ const dia = dif ? `(-${dif})` : "";
349 return `You currently have ${invites.value}${dia} invitations left.`;
349 return `You currently have ${invites.value}${dia} invitations left.`;
350 },
350 },
351 });
351 });
352
352
353 const invitesSent = this.state.invited.matchWith({
353 const invitesSent = this.state.invited.matchWith({
354− Nothing: () => <>{'Loading...'}</>,
354+ Nothing: () => <>{"Loading..."}</>,
355 Just: invited => {
355 Just: invited => {
356− const dit = dif ? `(+${dif})` : '';
356+ const dit = dif ? `(+${dif})` : "";
357 return (
357 return (
358 <>
358 <>
359− {'Out of the '}
359+ {"Out of the "}
360 {invited.value.total + dit}
360 {invited.value.total + dit}
361− {' invites you sent, '}
361+ {" invites you sent, "}
362 {invited.value.accepted}
362 {invited.value.accepted}
363− {' have been accepted.'}
363+ {" have been accepted."}
364 </>
364 </>
365 );
365 );
366 },
366 },
@@ -369,9 +369,9 @@ class InvitesSend extends React.Component {
369 const error = this.state.errors.matchWith({
369 const error = this.state.errors.matchWith({
370 Nothing: () => null,
370 Nothing: () => null,
371 Just: errors => (
371 Just: errors => (
372− <Warning className={'mt-8'}>
372+ <Warning className={"mt-8"}>
373 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
373 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
374− {'Something went wrong!'}
374+ {"Something went wrong!"}
375 </H3>
375 </H3>
376 {errors.value.map(err => (
376 {errors.value.map(err => (
377 <p>{err}</p>
377 <p>{err}</p>
@@ -382,30 +382,31 @@ class InvitesSend extends React.Component {
382
382
383 const fundingMessage = this.state.fundingMessage.matchWith({
383 const fundingMessage = this.state.fundingMessage.matchWith({
384 Nothing: () => null,
384 Nothing: () => null,
385− Just: msg => <Warning className={'mt-8'}>{msg.value}</Warning>,
385+ Just: msg => <Warning className={"mt-8"}>{msg.value}</Warning>,
386 });
386 });
387
387
388 let inputs;
388 let inputs;
389 if (this.state.status === STATUS.DONE) {
389 if (this.state.status === STATUS.DONE) {
390 //TODO proper clickable router thing
390 //TODO proper clickable router thing
391− inputs = '<- Back to Home';
391+ inputs = "<- Back to Home";
392 } else {
392 } else {
393 const inputDisabled = this.state.status !== STATUS.INPUT;
393 const inputDisabled = this.state.status !== STATUS.INPUT;
394
394
395 const lessButton = (
395 const lessButton = (
396 <Button
396 <Button
397− prop-size={'s narrow'}
397+ prop-size={"s narrow"}
398− className={'mt-8'}
398+ className={"mt-8"}
399 disabled={inputDisabled || this.state.targets.length === 1}
399 disabled={inputDisabled || this.state.targets.length === 1}
400− onClick={this.removeTarget}>
400+ onClick={this.removeTarget}
401+ >
401− {'-'}
402+ {"-"}
402 </Button>
403 </Button>
403 );
404 );
404
405
405 const moreButton = (
406 const moreButton = (
406 <Button
407 <Button
407− prop-size={'s narrow'}
408+ prop-size={"s narrow"}
408− className={'mt-8'}
409+ className={"mt-8"}
409 disabled={
410 disabled={
410 inputDisabled ||
411 inputDisabled ||
411 this.state.invitesAvailable.matchWith({
412 this.state.invitesAvailable.matchWith({
@@ -413,8 +414,9 @@ class InvitesSend extends React.Component {
413 Just: inv => inv.value <= this.state.targets.length,
414 Just: inv => inv.value <= this.state.targets.length,
414 })
415 })
415 }
416 }
416− onClick={this.addTarget}>
417+ onClick={this.addTarget}
418+ >
417− {'+'}
419+ {"+"}
418 </Button>
420 </Button>
419 );
421 );
420
422
@@ -422,7 +424,7 @@ class InvitesSend extends React.Component {
422 for (let i = 0; i < this.state.targets.length; i++) {
424 for (let i = 0; i < this.state.targets.length; i++) {
423 const target = this.state.targets[i];
425 const target = this.state.targets[i];
424 let progress = target.status.matchWith({
426 let progress = target.status.matchWith({
425− Nothing: () => '',
427+ Nothing: () => "",
426 Just: status => status.value,
428 Just: status => status.value,
427 });
429 });
428 targetInputs.push(
430 targetInputs.push(
@@ -432,23 +434,23 @@ class InvitesSend extends React.Component {
432 onChange={value => {
434 onChange={value => {
433 this.handleEmailInput(i, value);
435 this.handleEmailInput(i, value);
434 }}
436 }}
435− placeholder={'Email address'}
437+ placeholder={"Email address"}
436 disabled={inputDisabled}
438 disabled={inputDisabled}
437 />
439 />
438 {target.hasReceived.matchWith({
440 {target.hasReceived.matchWith({
439 Nothing: () => null,
441 Nothing: () => null,
440 Just: has =>
442 Just: has =>
441− has.value ? <span>{'×'}</span> : <span>{'°'}</span>,
443+ has.value ? <span>{"×"}</span> : <span>{"°"}</span>,
442 })}
444 })}
443 {progress}
445 {progress}
444− </>
446+ </>,
445 );
447 );
446 }
448 }
447
449
448 const submitButton = (
450 const submitButton = (
449 <Button
451 <Button
450− prop-size={'xl wide'}
452+ prop-size={"xl wide"}
451− className={'mt-8'}
453+ className={"mt-8"}
452 disabled={
454 disabled={
453 (inputDisabled && this.state.status !== STATUS.CAN_SEND) ||
455 (inputDisabled && this.state.status !== STATUS.CAN_SEND) ||
454 !this.canSend()
456 !this.canSend()
@@ -456,7 +458,8 @@ class InvitesSend extends React.Component {
456 onClick={() => {
458 onClick={() => {
457 if (this.state.status === STATUS.INPUT) this.generateInvites();
459 if (this.state.status === STATUS.INPUT) this.generateInvites();
458 if (this.state.status === STATUS.CAN_SEND) this.sendInvites();
460 if (this.state.status === STATUS.CAN_SEND) this.sendInvites();
459− }}>
461+ }}
462+ >
460 {this.state.status}
463 {this.state.status}
461 </Button>
464 </Button>
462 );
465 );
@@ -474,7 +477,9 @@ class InvitesSend extends React.Component {
474 return (
477 return (
475 <Row>
478 <Row>
476 <Col>
479 <Col>
477− <p>{'send invites here, for planets'}</p>
480+ <p>{"send invites here, for planets"}</p>
481+
482+ <p>{"send invites here, for planets"}</p>
478
483
479 <p>{invitesAvailable}</p>
484 <p>{invitesAvailable}</p>
480
485
src/bridge/views/IssueChild.js+46−42
@@ -1,8 +1,8 @@
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,
8 Row,
@@ -15,15 +15,15 @@ import {
15 PointInput,
15 PointInput,
16 AddressInput,
16 AddressInput,
17 InnerLabel,
17 InnerLabel,
18−} from '../components/Base';
18+} from "../components/Base";
19
19
20−import StatelessTransaction from '../components/StatelessTransaction';
20+import StatelessTransaction from "../components/StatelessTransaction";
21
21
22−import { NETWORK_NAMES } from '../lib/network';
22+import { NETWORK_NAMES } from "../lib/network";
23−import { getSpawnCandidate } from '../lib/child';
23+import { getSpawnCandidate } from "../lib/child";
24−import { canDecodePatp } from '../lib/txn';
24+import { canDecodePatp } from "../lib/txn";
25
25
26−import { isValidAddress } from '../lib/wallet';
26+import { isValidAddress } from "../lib/wallet";
27
27
28const setFind = (set, pred) => {
28const setFind = (set, pred) => {
29 for (const e of set) {
29 for (const e of set) {
@@ -50,13 +50,13 @@ class IssueChild extends React.Component {
50 ];
50 ];
51
51
52 this.state = {
52 this.state = {
53− receivingAddress: '',
53+ receivingAddress: "",
54 issuingPoint: issuingPoint,
54 issuingPoint: issuingPoint,
55− desiredPoint: '',
55+ desiredPoint: "",
56 isAvailable: Nothing(), // use Nothing to allow attempt when offline
56 isAvailable: Nothing(), // use Nothing to allow attempt when offline
57 suggestions: suggestions,
57 suggestions: suggestions,
58 validChildren: new Set(),
58 validChildren: new Set(),
59− autoComplete: '',
59+ autoComplete: "",
60 };
60 };
61
61
62 this.handlePointInput = this.handlePointInput.bind(this);
62 this.handlePointInput = this.handlePointInput.bind(this);
@@ -84,9 +84,9 @@ class IssueChild extends React.Component {
84 if (desiredPoint.length < 15) {
84 if (desiredPoint.length < 15) {
85 const suggestedPoint =
85 const suggestedPoint =
86 setFind(this.state.validChildren, e => e.startsWith(desiredPoint)) ||
86 setFind(this.state.validChildren, e => e.startsWith(desiredPoint)) ||
87− '';
87+ "";
88 const rendered =
88 const rendered =
89− ' '.repeat(desiredPoint.length) +
89+ " ".repeat(desiredPoint.length) +
90 suggestedPoint.substring(desiredPoint.length);
90 suggestedPoint.substring(desiredPoint.length);
91
91
92 const available = Just(this.state.validChildren.has(desiredPoint));
92 const available = Just(this.state.validChildren.has(desiredPoint));
@@ -114,7 +114,7 @@ class IssueChild extends React.Component {
114 const txn = ecliptic.spawn(
114 const txn = ecliptic.spawn(
115 validContracts,
115 validContracts,
116 pointDec,
116 pointDec,
117− state.receivingAddress
117+ state.receivingAddress,
118 );
118 );
119
119
120 return Just(txn);
120 return Just(txn);
@@ -156,14 +156,14 @@ class IssueChild extends React.Component {
156
156
157 const esdomain =
157 const esdomain =
158 props.networkType === NETWORK_NAMES.ROPSTEN
158 props.networkType === NETWORK_NAMES.ROPSTEN
159− ? 'ropsten.etherscan.io'
159+ ? "ropsten.etherscan.io"
160− : 'etherscan.io';
160+ : "etherscan.io";
161
161
162 return (
162 return (
163 <Row>
163 <Row>
164 <Col>
164 <Col>
165 <H1>
165 <H1>
166− {'Issue a Child From '}{' '}
166+ {"Issue a Child From "}{" "}
167 <code>{`${ob.patp(state.issuingPoint)}`}</code>
167 <code>{`${ob.patp(state.issuingPoint)}`}</code>
168 </H1>
168 </H1>
169
169
@@ -177,36 +177,38 @@ class IssueChild extends React.Component {
177 {`Your point can only issue children with particular names. Some
177 {`Your point can only issue children with particular names. Some
178 valid suggestions for `}
178 valid suggestions for `}
179 {<code>{ob.patp(state.issuingPoint)}</code>}
179 {<code>{ob.patp(state.issuingPoint)}</code>}
180− {' are '}
180+ {" are "}
181 <code>{state.suggestions[0]}</code>
181 <code>{state.suggestions[0]}</code>
182− {', '}
182+ {", "}
183 <code>{state.suggestions[1]}</code>
183 <code>{state.suggestions[1]}</code>
184− {', and '}
184+ {", and "}
185 <code>{state.suggestions[2]}</code>
185 <code>{state.suggestions[2]}</code>
186− {'.'}
186+ {"."}
187 </P>
187 </P>
188
188
189 <PointInput
189 <PointInput
190 prop-size="lg"
190 prop-size="lg"
191 prop-format="innerLabel"
191 prop-format="innerLabel"
192− className={'mono mt-8'}
192+ className={"mono mt-8"}
193 placeholder={`e.g. ${state.suggestions[3]}`}
193 placeholder={`e.g. ${state.suggestions[3]}`}
194 value={state.desiredPoint}
194 value={state.desiredPoint}
195− onChange={this.handlePointInput}>
195+ onChange={this.handlePointInput}
196+ >
196− <InnerLabel>{'Point to Issue'}</InnerLabel>
197+ <InnerLabel>{"Point to Issue"}</InnerLabel>
197 <div
198 <div
198 style={{
199 style={{
199− marginTop: '8.8rem',
200+ marginTop: "8.8rem",
200− marginLeft: '4.6rem',
201+ marginLeft: "4.6rem",
201− fontSize: '5rem',
202+ fontSize: "5rem",
202− color: '#757575',
203+ color: "#757575",
203− whiteSpace: 'pre-wrap',
204+ whiteSpace: "pre-wrap",
204 }}
205 }}
205− className="abs tl-0 mono">
206+ className="abs tl-0 mono"
207+ >
206 {state.autoComplete}
208 {state.autoComplete}
207 </div>
209 </div>
208 <ValidatedSigil
210 <ValidatedSigil
209− className={'tr-0 mt-05 mr-0 abs'}
211+ className={"tr-0 mt-05 mr-0 abs"}
210 patp={state.desiredPoint}
212 patp={state.desiredPoint}
211 size={68}
213 size={68}
212 margin={8}
214 margin={8}
@@ -223,20 +225,22 @@ class IssueChild extends React.Component {
223 disabled={
225 disabled={
224 Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
226 Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
225 }
227 }
226− onChange={v => this.handleAddressInput(v)}>
228+ onChange={v => this.handleAddressInput(v)}
229+ >
227− <InnerLabel>{'Receiving Address'}</InnerLabel>
230+ <InnerLabel>{"Receiving Address"}</InnerLabel>
228− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
231+ <ShowBlockie className={"mt-1"} address={state.receivingAddress} />
229 </AddressInput>
232 </AddressInput>
230
233
231 <Anchor
234 <Anchor
232− className={'mt-1'}
235+ className={"mt-1"}
233− prop-size={'sm'}
236+ prop-size={"sm"}
234 prop-disabled={
237 prop-disabled={
235 !isValidAddress(state.receivingAddress) || !esvisible
238 !isValidAddress(state.receivingAddress) || !esvisible
236 }
239 }
237− target={'_blank'}
240+ target={"_blank"}
238− href={`https://${esdomain}/address/${state.receivingAddress}`}>
241+ href={`https://${esdomain}/address/${state.receivingAddress}`}
242+ >
239− {'View on Etherscan ↗'}
243+ {"View on Etherscan ↗"}
240 </Anchor>
244 </Anchor>
241
245
242 <StatelessTransaction
246 <StatelessTransaction
src/bridge/views/Keystore.js+39−37
@@ -1,14 +1,15 @@
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/routeNames";
11+import { withHistory } from "../lib/history";
11−import { EthereumWallet } from '../lib/wallet';
12+import { EthereumWallet } from "../lib/wallet";
12
13
13class Keystore extends React.Component {
14class Keystore extends React.Component {
14 constructor(props) {
15 constructor(props) {
@@ -16,7 +17,7 @@ class Keystore extends React.Component {
16
17
17 this.state = {
18 this.state = {
18 keystore: Nothing(), // Maybe<Result<String, String>>
19 keystore: Nothing(), // Maybe<Result<String, String>>
19− password: '',
20+ password: "",
20 decryptionProblem: false,
21 decryptionProblem: false,
21 };
22 };
22
23
@@ -69,7 +70,7 @@ class Keystore extends React.Component {
69 };
70 };
70
71
71 const failure = _ => {
72 const failure = _ => {
72− const message = 'There was a problem uploading your Keystore file';
73+ const message = "There was a problem uploading your Keystore file";
73 this.setState({ keystore: Just(Error(message)) });
74 this.setState({ keystore: Just(Error(message)) });
74 };
75 };
75
76
@@ -80,15 +81,15 @@ class Keystore extends React.Component {
80 };
81 };
81
82
82 render() {
83 render() {
83− const { pushRoute, popRoute, wallet } = this.props;
84+ const { history, wallet } = this.props;
84 const { keystore, password, decryptionProblem } = this.state;
85 const { keystore, password, decryptionProblem } = this.state;
85
86
86 const uploadButtonClass = keystore.matchWith({
87 const uploadButtonClass = keystore.matchWith({
87− Nothing: _ => 'bg-blue white',
88+ Nothing: _ => "bg-blue white",
88 Just: ks =>
89 Just: ks =>
89 ks.value.matchWith({
90 ks.value.matchWith({
90− Ok: _ => 'bg-green white',
91+ Ok: _ => "bg-green white",
91− Error: _ => 'bg-yellow black',
92+ Error: _ => "bg-yellow black",
92 }),
93 }),
93 });
94 });
94
95
@@ -96,18 +97,18 @@ class Keystore extends React.Component {
96 decryptionProblem === false ? (
97 decryptionProblem === false ? (
97 <div />
98 <div />
98 ) : (
99 ) : (
99− <Warning className={'mt-8'}>
100+ <Warning className={"mt-8"}>
100 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
101 <H3 style={{ marginTop: 0, paddingTop: 0 }}>
101 {"Couldn't decrypt wallet."}
102 {"Couldn't decrypt wallet."}
102 </H3>
103 </H3>
103− {'You may have entered an incorrect password.'}
104+ {"You may have entered an incorrect password."}
104 </Warning>
105 </Warning>
105 );
106 );
106
107
107 return (
108 return (
108 <Row>
109 <Row>
109− <Col className={'measure-md'}>
110+ <Col className={"measure-md"}>
110− <H1 className={'mb-4'}>{'Upload Your Keystore File'}</H1>
111+ <H1 className={"mb-4"}>{"Upload Your Keystore File"}</H1>
111 <InputCaption>
112 <InputCaption>
112 {`Please upload your Ethereum keystore file. If your keystore
113 {`Please upload your Ethereum keystore file. If your keystore
113 file is encrypted with a password, you'll also need to enter
114 file is encrypted with a password, you'll also need to enter
@@ -116,9 +117,10 @@ class Keystore extends React.Component {
116
117
117 <UploadButton
118 <UploadButton
118 className={`btn ${uploadButtonClass} mt-10`}
119 className={`btn ${uploadButtonClass} mt-10`}
119− onChange={this.handleKeystoreUpload}>
120+ onChange={this.handleKeystoreUpload}
121+ >
120− <div className={'flex-center-all fs-4 h-11 pointer'}>
122+ <div className={"flex-center-all fs-4 h-11 pointer"}>
121− {'Upload Keystore file'}
123+ {"Upload Keystore file"}
122 </div>
124 </div>
123 </UploadButton>
125 </UploadButton>
124
126
@@ -131,29 +133,29 @@ class Keystore extends React.Component {
131 onChange={this.handlePasswordInput}
133 onChange={this.handlePasswordInput}
132 value={password}
134 value={password}
133 autocomplete="off"
135 autocomplete="off"
134− autoFocus>
136+ autoFocus
137+ >
135− <InnerLabel>{'Password'}</InnerLabel>
138+ <InnerLabel>{"Password"}</InnerLabel>
136 </Input>
139 </Input>
137
140
138 <Button
141 <Button
139 className={` mt-10`}
142 className={` mt-10`}
140− prop-size={'wide lg'}
143+ prop-size={"wide lg"}
141 disabled={Nothing.hasInstance(keystore)}
144 disabled={Nothing.hasInstance(keystore)}
142− onClick={this.constructWallet}>
145+ onClick={this.constructWallet}
146+ >
143− {'Decrypt'}
147+ {"Decrypt"}
144 </Button>
148 </Button>
145
149
146 {decryptMessage}
150 {decryptMessage}
147
151
148 <Button
152 <Button
149− className={'mt-10'}
153+ className={"mt-10"}
150− prop-size={'wide lg'}
154+ prop-size={"wide lg"}
151 disabled={Nothing.hasInstance(wallet)}
155 disabled={Nothing.hasInstance(wallet)}
152− onClick={() => {
153− popRoute();
154− pushRoute(ROUTE_NAMES.SHIPS);
156+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
155− }}>
157+ >
156− {'Continue →'}
158+ {"Continue →"}
157 </Button>
159 </Button>
158 </Col>
160 </Col>
159 </Row>
161 </Row>
@@ -161,4 +163,4 @@ class Keystore extends React.Component {
161 }
163 }
162}
164}
163
165
164−export default Keystore;
166+export default withHistory(Keystore);
src/bridge/views/Landing.js+69−61
@@ -1,72 +1,80 @@
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/routeNames";
7+import { useHistory } from "../lib/history";
7
8
8−const Landing = props => (
9+function Landing(props) {
9− <Row>
10+ const history = useHistory();
10− <Col>
11− <H1>{'Welcome'}</H1>
12
11
13− <P>
12+ return (
14− {'Bridge is a tool for managing and viewing assets on ' +
13+ <Row>
15− 'Azimuth, the Urbit address space.'}
14+ <Col>
16− </P>
15+ <H1>{"Welcome"}</H1>
17
16
18− <Button
17+ <P>
19− prop-type="link"
18+ {"Bridge is a tool for managing and viewing assets on " +
20− prop-size="lg"
19+ "Azimuth, the Urbit address space."}
21− className={'mb-4 mt-8'}
20+ </P>
22− onClick={() => props.pushRoute(ROUTE_NAMES.INVITE_TICKET)}>
23− {'Claim an invite →'}
24− </Button>
25
21
26− <P>{'If you were sent an Azimuth invite code, start here.'}</P>
22+ <Button
23+ prop-type="link"
24+ prop-size="lg"
25+ className={"mb-4 mt-8"}
26+ onClick={() => history.push(ROUTE_NAMES.INVITE_TICKET)}
27+ >
28+ {"Claim an invite →"}
29+ </Button>
27
30
28− <Button
31+ <P>{"If you were sent an Azimuth invite code, start here."}</P>
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>
35
32
36− <P>
33+ <Button
37− {'If you own Azimuth assets and want to manage them in some ' +
34+ prop-type="link"
38− "way, start here. You'll need either your Urbit ticket or a " +
35+ prop-size="lg"
39− 'keypair.'}
36+ className={"mb-4 mt-8"}
40− </P>
37+ onClick={() => history.push(ROUTE_NAMES.NETWORK)}
38+ >
39+ {"Unlock a Wallet →"}
40+ </Button>
41
41
42− {
42+ <P>
43− <Row>
43+ {"If you own Azimuth assets and want to manage them in some " +
44− <Col>
44+ "way, start here. You'll need either your Urbit ticket or a " +
45− <Button
45+ "keypair."}
46− prop-type={'link'}
46+ </P>
47− prop-size={'lg'}
48− className={'mb-4 mt-8'}
49− onClick={() => props.pushRoute(ROUTE_NAMES.VIEW_SHIP)}>
50− {'View a point →'}
51− </Button>
52− <P>{'View an Azimuth point without signing into a wallet.'}</P>
53− </Col>
54− </Row>
55− // FIXME add address view
56− // <LinkButton
57− // size={'l'}
58− // className={'mb-4 mt-10'}
59− // onClick={ () => props.pushRoute(ROUTE_NAMES.NETWORK) }
60− // >
61− // { 'View an Address →' }
62− // </LinkButton>
63
47
48+ {
49+ <Row>
50+ <Col>
51+ <Button
52+ prop-type={"link"}
53+ prop-size={"lg"}
54+ className={"mb-4 mt-8"}
55+ onClick={() => history.push(ROUTE_NAMES.VIEW_SHIP)}
56+ >
57+ {"View a point →"}
58+ </Button>
59+ <P>{"View an Azimuth point without signing into a wallet."}</P>
60+ </Col>
61+ </Row>
62+ // FIXME add address view
63+ // <LinkButton
64+ // size={'l'}
65+ // className={'mb-4 mt-10'}
66+ // onClick={ () => history.push(ROUTE_NAMES.NETWORK) }
67+ // >
68+ // { 'View an Address →' }
69+ // </LinkButton>
70+
64− // <p>
71+ // <p>
65− // { "Lorem" }
72+ // { "Lorem" }
66− // </p>
73+ // </p>
67− }
74+ }
68− </Col>
75+ </Col>
69− </Row>
76+ </Row>
70−);
77+ );
78+}
71
79
72export default Landing;
80export default Landing;
src/bridge/views/Ledger.js+69−67
@@ -1,19 +1,20 @@
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/routeNames";
13+import { withHistory } from "../lib/history";
13
14
14−const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
15+const chopHdPrefix = str => (str.slice(0, 2) === "m/" ? str.slice(2) : str);
15
16
16−const addHdPrefix = str => (str.slice(0, 2) === 'm/' ? str : 'm/' + str);
17+const addHdPrefix = str => (str.slice(0, 2) === "m/" ? str : "m/" + str);
17
18
18class Ledger extends React.Component {
19class Ledger extends React.Component {
19 constructor(props) {
20 constructor(props) {
@@ -42,7 +43,7 @@ class Ledger extends React.Component {
42
43
43 updateHdPath(basePath, account) {
44 updateHdPath(basePath, account) {
44 let hdpath = this.state.hdpath;
45 let hdpath = this.state.hdpath;
45− if (basePath !== 'custom') {
46+ if (basePath !== "custom") {
46 hdpath = basePath.replace(/x/g, account);
47 hdpath = basePath.replace(/x/g, account);
47 }
48 }
48 this.setState({ basePath, account, hdpath });
49 this.setState({ basePath, account, hdpath });
@@ -62,8 +63,8 @@ class Ledger extends React.Component {
62
63
63 eth.getAddress(path, false, true).then(
64 eth.getAddress(path, false, true).then(
64 info => {
65 info => {
65− const publicKey = Buffer.from(info.publicKey, 'hex');
66+ const publicKey = Buffer.from(info.publicKey, "hex");
66− const chainCode = Buffer.from(info.chainCode, 'hex');
67+ const chainCode = Buffer.from(info.chainCode, "hex");
67 const pub = secp256k1.publicKeyConvert(publicKey, true);
68 const pub = secp256k1.publicKeyConvert(publicKey, true);
68 const hd = bip32.fromPublicKey(pub, chainCode);
69 const hd = bip32.fromPublicKey(pub, chainCode);
69 setWallet(Maybe.Just(hd));
70 setWallet(Maybe.Just(hd));
@@ -71,21 +72,21 @@ class Ledger extends React.Component {
71 },
72 },
72 _ => {
73 _ => {
73 setWallet(Maybe.Nothing());
74 setWallet(Maybe.Nothing());
74− }
75+ },
75 );
76 );
76 }
77 }
77
78
78 render() {
79 render() {
79− const { pushRoute, popRoute, wallet } = this.props;
80+ const { history, wallet } = this.props;
80 const { basePath, hdpath, account } = this.state;
81 const { basePath, hdpath, account } = this.state;
81 const { handlePathSelection, handleAccountSelection } = this;
82 const { handlePathSelection, handleAccountSelection } = this;
82
83
83 const pathOptions = [
84 const pathOptions = [
84− { title: 'Ledger Live', value: LEDGER_LIVE_PATH },
85+ { title: "Ledger Live", value: LEDGER_LIVE_PATH },
85− { title: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
86+ { title: "Ledger Legacy", value: LEDGER_LEGACY_PATH },
86 {
87 {
87− title: 'Custom path',
88+ title: "Custom path",
88− value: 'custom',
89+ value: "custom",
89 },
90 },
90 ];
91 ];
91 let basePathTitle = pathOptions.find(o => o.value === basePath).title;
92 let basePathTitle = pathOptions.find(o => o.value === basePath).title;
@@ -93,7 +94,7 @@ class Ledger extends React.Component {
93 let accountOptions = [];
94 let accountOptions = [];
94 for (let i = 0; i < 20; i++) {
95 for (let i = 0; i < 20; i++) {
95 accountOptions.push({
96 accountOptions.push({
96− title: 'Account #' + (i + 1),
97+ title: "Account #" + (i + 1),
97 value: i,
98 value: i,
98 });
99 });
99 }
100 }
@@ -104,14 +105,14 @@ class Ledger extends React.Component {
104 className="mt-8"
105 className="mt-8"
105 options={pathOptions}
106 options={pathOptions}
106 handleUpdate={handlePathSelection}
107 handleUpdate={handlePathSelection}
107− title={'Derivation path'}
108+ title={"Derivation path"}
108 currentSelectionTitle={basePathTitle}
109 currentSelectionTitle={basePathTitle}
109 fullWidth={true}
110 fullWidth={true}
110 />
111 />
111 );
112 );
112
113
113 const truePathSelection =
114 const truePathSelection =
114− basePath === 'custom' ? (
115+ basePath === "custom" ? (
115 <Input
116 <Input
116 className="pt-8 mt-4 text-mono"
117 className="pt-8 mt-4 text-mono"
117 prop-size="md"
118 prop-size="md"
@@ -119,8 +120,9 @@ class Ledger extends React.Component {
119 name="hdpath"
120 name="hdpath"
120 value={addHdPrefix(hdpath)}
121 value={addHdPrefix(hdpath)}
121 autocomplete="off"
122 autocomplete="off"
122− onChange={this.handleHdPathInput}>
123+ onChange={this.handleHdPathInput}
124+ >
123− <InnerLabel>{'HD Path'}</InnerLabel>
125+ <InnerLabel>{"HD Path"}</InnerLabel>
124 </Input>
126 </Input>
125 ) : (
127 ) : (
126 <InnerLabelDropdown
128 <InnerLabelDropdown
@@ -129,7 +131,7 @@ class Ledger extends React.Component {
129 prop-format="innerLabel"
131 prop-format="innerLabel"
130 options={accountOptions}
132 options={accountOptions}
131 handleUpdate={handleAccountSelection}
133 handleUpdate={handleAccountSelection}
132− title={'Account'}
134+ title={"Account"}
133 currentSelectionTitle={accountTitle}
135 currentSelectionTitle={accountTitle}
134 fullWidth={true}
136 fullWidth={true}
135 />
137 />
@@ -137,10 +139,10 @@ class Ledger extends React.Component {
137
139
138 return (
140 return (
139 <Row>
141 <Row>
140− <Col className={'measure-md'}>
142+ <Col className={"measure-md"}>
141− <H1>{'Authenticate With Your Ledger'}</H1>
143+ <H1>{"Authenticate With Your Ledger"}</H1>
142
144
143− <H2>{'Running on HTTPS?'}</H2>
145+ <H2>{"Running on HTTPS?"}</H2>
144
146
145 <P>
147 <P>
146 {`Connect and authenticate to your Ledger, and then open the
148 {`Connect and authenticate to your Ledger, and then open the
@@ -155,41 +157,42 @@ class Ledger extends React.Component {
155 it below.`}
157 it below.`}
156 </P>
158 </P>
157
159
158− <H2>{'Running on HTTP?'}</H2>
160+ <H2>{"Running on HTTP?"}</H2>
159
161
160 <P>{`To authenticate and sign transactions with a Ledger, Bridge
162 <P>{`To authenticate and sign transactions with a Ledger, Bridge
161 must be serving over HTTPS on localhost. You can do this via the
163 must be serving over HTTPS on localhost. You can do this via the
162 following:`}</P>
164 following:`}</P>
163
165
164− <ol className={'measure-md'}>
166+ <ol className={"measure-md"}>
165− <li className={'mt-4'}>
167+ <li className={"mt-4"}>
166− {'Install'}{' '}
168+ {"Install"}{" "}
167 <a
169 <a
168− target={'_blank'}
170+ target={"_blank"}
169− href={'https://github.com/FiloSottile/mkcert'}>
171+ href={"https://github.com/FiloSottile/mkcert"}
172+ >
170− {'mkcert'}
173+ {"mkcert"}
171 </a>
174 </a>
172 </li>
175 </li>
173− <li className={'mt-4'}>
176+ <li className={"mt-4"}>
174− {'Install a local certificate authority via '}
177+ {"Install a local certificate authority via "}
175− <code>{'mkcert -install'}</code>
178+ <code>{"mkcert -install"}</code>
176 </li>
179 </li>
177− <li className={'mt-4'}>
180+ <li className={"mt-4"}>
178− {'In your '}
181+ {"In your "}
179− <code>{'bridge'}</code>
182+ <code>{"bridge"}</code>
180 {
183 {
181− ' directory, generate a certificate valid for localhost via '
184+ " directory, generate a certificate valid for localhost via "
182− }{' '}
185+ }{" "}
183− <code>{'mkcert localhost'}</code>
186+ <code>{"mkcert localhost"}</code>
184− {'.'}
187+ {"."}
185− {'This will produce two files: '}
188+ {"This will produce two files: "}
186− <code>{'localhost.pem'}</code> {', the local certificate, and '}
189+ <code>{"localhost.pem"}</code> {", the local certificate, and "}
187− <code>{'localhost-key.pem'}</code>
190+ <code>{"localhost-key.pem"}</code>
188− {', its corresponding private key.'}
191+ {", its corresponding private key."}
189 </li>
192 </li>
190− <li className={'mt-4'}>
193+ <li className={"mt-4"}>
191− {'Run '}
194+ {"Run "}
192− <code>{'python bridge-https.py'}</code>
195+ <code>{"python bridge-https.py"}</code>
193 </li>
196 </li>
194 </ol>
197 </ol>
195
198
@@ -197,21 +200,20 @@ class Ledger extends React.Component {
197 {truePathSelection}
200 {truePathSelection}
198
201
199 <Button
202 <Button
200− prop-size={'wide lg'}
203+ prop-size={"wide lg"}
201− className={'mt-8'}
204+ className={"mt-8"}
202− onClick={this.pollDevice}>
205+ onClick={this.pollDevice}
206+ >
203− {'Authenticate →'}
207+ {"Authenticate →"}
204 </Button>
208 </Button>
205
209
206 <Button
210 <Button
207− className={'mt-8'}
211+ className={"mt-8"}
208− prop-size={'wide lg'}
212+ prop-size={"wide lg"}
209 disabled={Maybe.Nothing.hasInstance(wallet)}
213 disabled={Maybe.Nothing.hasInstance(wallet)}
210− onClick={() => {
211− popRoute();
212− pushRoute(ROUTE_NAMES.SHIPS);
214+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
213− }}>
215+ >
214− {'Continue →'}
216+ {"Continue →"}
215 </Button>
217 </Button>
216 </Col>
218 </Col>
217 </Row>
219 </Row>
@@ -219,4 +221,4 @@ class Ledger extends React.Component {
219 }
221 }
220}
222}
221
223
222−export default Ledger;
224+export default withHistory(Ledger);
src/bridge/views/Mnemonic.js+34−32
@@ -1,25 +1,26 @@
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
12
13−import { ROUTE_NAMES } from '../lib/router';
13+import { ROUTE_NAMES } from "../lib/routeNames";
14+import { withHistory } from "../lib/history";
14−import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet';
15+import { DEFAULT_HD_PATH, walletFromMnemonic } from "../lib/wallet";
15
16
16class Mnemonic extends React.Component {
17class Mnemonic extends React.Component {
17 constructor(props) {
18 constructor(props) {
18 super(props);
19 super(props);
19
20
20 this.state = {
21 this.state = {
21− mnemonic: '',
22+ mnemonic: "",
22− passphrase: '',
23+ passphrase: "",
23 hdpath: DEFAULT_HD_PATH,
24 hdpath: DEFAULT_HD_PATH,
24 };
25 };
25
26
@@ -43,8 +44,8 @@ class Mnemonic extends React.Component {
43 const hdpath = state.hdpath;
44 const hdpath = state.hdpath;
44 this.attemptWalletDerivation(
45 this.attemptWalletDerivation(
45 mnemonic,
46 mnemonic,
46− hdpath === '' ? DEFAULT_HD_PATH : hdpath,
47+ hdpath === "" ? DEFAULT_HD_PATH : hdpath,
47− passphrase
48+ passphrase,
48 );
49 );
49 return {
50 return {
50 passphrase,
51 passphrase,
@@ -58,8 +59,8 @@ class Mnemonic extends React.Component {
58 const passphrase = state.passphrase;
59 const passphrase = state.passphrase;
59 this.attemptWalletDerivation(
60 this.attemptWalletDerivation(
60 mnemonic,
61 mnemonic,
61− hdpath === '' ? DEFAULT_HD_PATH : hdpath,
62+ hdpath === "" ? DEFAULT_HD_PATH : hdpath,
62− passphrase
63+ passphrase,
63 );
64 );
64 return {
65 return {
65 mnemonic,
66 mnemonic,
@@ -73,8 +74,8 @@ class Mnemonic extends React.Component {
73 const passphrase = state.passphrase;
74 const passphrase = state.passphrase;
74 this.attemptWalletDerivation(
75 this.attemptWalletDerivation(
75 mnemonic,
76 mnemonic,
76− hdpath === '' ? DEFAULT_HD_PATH : hdpath,
77+ hdpath === "" ? DEFAULT_HD_PATH : hdpath,
77− passphrase
78+ passphrase,
78 );
79 );
79 return {
80 return {
80 hdpath,
81 hdpath,
@@ -91,15 +92,15 @@ class Mnemonic extends React.Component {
91 }
92 }
92
93
93 render() {
94 render() {
94− const { pushRoute, popRoute, wallet } = this.props;
95+ const { history, wallet } = this.props;
95 const { mnemonic, hdpath, exampleMnemonic, passphrase } = this.state;
96 const { mnemonic, hdpath, exampleMnemonic, passphrase } = this.state;
96
97
97 return (
98 return (
98 <Row>
99 <Row>
99 <Col>
100 <Col>
100− <H1 className={'mb-4'}>{'Enter Your Mnemonic'}</H1>
101+ <H1 className={"mb-4"}>{"Enter Your Mnemonic"}</H1>
101 <InputCaption>
102 <InputCaption>
102− {'Please enter your BIP39 mnemonic here.'}
103+ {"Please enter your BIP39 mnemonic here."}
103 </InputCaption>
104 </InputCaption>
104
105
105 <MnemonicInput
106 <MnemonicInput
@@ -112,8 +113,9 @@ class Mnemonic extends React.Component {
112 onChange={this.handleMnemonicInput}
113 onChange={this.handleMnemonicInput}
113 value={mnemonic}
114 value={mnemonic}
114 autocomplete="off"
115 autocomplete="off"
115− autoFocus>
116+ autoFocus
117+ >
116− <InnerLabel>{'Mnemonic'}</InnerLabel>
118+ <InnerLabel>{"Mnemonic"}</InnerLabel>
117 </MnemonicInput>
119 </MnemonicInput>
118
120
119 <InputCaption>
121 <InputCaption>
@@ -128,8 +130,9 @@ class Mnemonic extends React.Component {
128 type="password"
130 type="password"
129 value={passphrase}
131 value={passphrase}
130 autocomplete="off"
132 autocomplete="off"
131− onChange={this.handlePassphraseInput}>
133+ onChange={this.handlePassphraseInput}
134+ >
132− <InnerLabel>{'Passphrase'}</InnerLabel>
135+ <InnerLabel>{"Passphrase"}</InnerLabel>
133 </Input>
136 </Input>
134
137
135 <InputCaption>
138 <InputCaption>
@@ -143,18 +146,17 @@ class Mnemonic extends React.Component {
143 name="hdpath"
146 name="hdpath"
144 value={hdpath}
147 value={hdpath}
145 autocomplete="off"
148 autocomplete="off"
146− onChange={this.handleHdPathInput}>
149+ onChange={this.handleHdPathInput}
150+ >
147− <InnerLabel>{'HD Path'}</InnerLabel>
151+ <InnerLabel>{"HD Path"}</InnerLabel>
148 </Input>
152 </Input>
149
153
150 <Button
154 <Button
151− className={'mt-10'}
155+ className={"mt-10"}
152 disabled={Nothing.hasInstance(wallet)}
156 disabled={Nothing.hasInstance(wallet)}
153− onClick={() => {
154− popRoute();
155− pushRoute(ROUTE_NAMES.SHIPS);
157+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
156− }}>
158+ >
157− {'Continue →'}
159+ {"Continue →"}
158 </Button>
160 </Button>
159 </Col>
161 </Col>
160 </Row>
162 </Row>
@@ -162,4 +164,4 @@ class Mnemonic extends React.Component {
162 }
164 }
163}
165}
164
166
165−export default Mnemonic;
167+export default withHistory(Mnemonic);
src/bridge/views/Network.js+31−29
@@ -1,14 +1,15 @@
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/routeNames";
12+import { withHistory } from "../lib/history";
12
13
13class Network extends React.Component {
14class Network extends React.Component {
14 constructor(props) {
15 constructor(props) {
@@ -23,7 +24,7 @@ class Network extends React.Component {
23 if (network === NETWORK_NAMES.LOCAL) {
24 if (network === NETWORK_NAMES.LOCAL) {
24 setNetworkType(network);
25 setNetworkType(network);
25
26
26− const endpoint = 'ws://localhost:8545';
27+ const endpoint = "ws://localhost:8545";
27 const provider = new Web3.providers.WebsocketProvider(endpoint);
28 const provider = new Web3.providers.WebsocketProvider(endpoint);
28 const web3 = new Web3(provider);
29 const web3 = new Web3(provider);
29 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
30 const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
@@ -73,11 +74,11 @@ class Network extends React.Component {
73
74
74 // Note: localhost:3456 doesn't actually point to anything, we just need
75 // Note: localhost:3456 doesn't actually point to anything, we just need
75 // a provider to initialize the Web3 object
76 // a provider to initialize the Web3 object
76− const provider = new Web3.providers.HttpProvider('http://localhost:3456');
77+ const provider = new Web3.providers.HttpProvider("http://localhost:3456");
77 const web3 = new Web3(provider);
78 const web3 = new Web3(provider);
78
79
79 const target =
80 const target =
80− process.env.NODE_ENV === 'development'
81+ process.env.NODE_ENV === "development"
81 ? CONTRACT_ADDRESSES.DEV
82 ? CONTRACT_ADDRESSES.DEV
82 : CONTRACT_ADDRESSES.MAINNET;
83 : CONTRACT_ADDRESSES.MAINNET;
83
84
@@ -90,54 +91,55 @@ class Network extends React.Component {
90 getNetworkOptions() {
91 getNetworkOptions() {
91 return [
92 return [
92 {
93 {
93− title: 'Main Network (default)',
94+ title: "Main Network (default)",
94 value: NETWORK_NAMES.MAINNET,
95 value: NETWORK_NAMES.MAINNET,
95 },
96 },
96 {
97 {
97− title: 'Local Node',
98+ title: "Local Node",
98 value: NETWORK_NAMES.LOCAL,
99 value: NETWORK_NAMES.LOCAL,
99 },
100 },
100 {
101 {
101− title: 'Ropsten',
102+ title: "Ropsten",
102 value: NETWORK_NAMES.ROPSTEN,
103 value: NETWORK_NAMES.ROPSTEN,
103 },
104 },
104 {
105 {
105− type: 'divider',
106+ type: "divider",
106 },
107 },
107 {
108 {
108− title: 'Offline',
109+ title: "Offline",
109 value: NETWORK_NAMES.OFFLINE,
110 value: NETWORK_NAMES.OFFLINE,
110 },
111 },
111 ];
112 ];
112 }
113 }
113
114
114 render() {
115 render() {
115− const { networkType, pushRoute } = this.props;
116+ const { networkType, history } = this.props;
116 const networkOptions = this.getNetworkOptions();
117 const networkOptions = this.getNetworkOptions();
117
118
118 return (
119 return (
119 <Row>
120 <Row>
120 <Col>
121 <Col>
121− <H1>{'Select Network'}</H1>
122+ <H1>{"Select Network"}</H1>
122
123
123 <P>
124 <P>
124 {"Please select the Ethereum node you'd like to send " +
125 {"Please select the Ethereum node you'd like to send " +
125− 'transactions to. For highly valuable keys, please select ' +
126+ "transactions to. For highly valuable keys, please select " +
126− 'offline mode.'}
127+ "offline mode."}
127 </P>
128 </P>
128
129
129 <InnerLabelDropdown
130 <InnerLabelDropdown
130− className={'mt-6'}
131+ className={"mt-6"}
131− title={'Node:'}
132+ title={"Node:"}
132 handleUpdate={this.handleNetworkChange}
133 handleUpdate={this.handleNetworkChange}
133 options={networkOptions}
134 options={networkOptions}
134 currentSelectionTitle={renderNetworkType(networkType)}
135 currentSelectionTitle={renderNetworkType(networkType)}
135 />
136 />
136
137
137 <Button
138 <Button
138− className={'mt-10'}
139+ className={"mt-10"}
139− onClick={() => pushRoute(ROUTE_NAMES.WALLET)}>
140+ onClick={() => history.push(ROUTE_NAMES.WALLET)}
141+ >
140− {'Continue →'}
142+ {"Continue →"}
141 </Button>
143 </Button>
142 </Col>
144 </Col>
143 </Row>
145 </Row>
@@ -145,4 +147,4 @@ class Network extends React.Component {
145 }
147 }
146}
148}
147
149
148−export default Network;
150+export default withHistory(Network);
src/bridge/views/Point.js+20−32
@@ -1,15 +1,16 @@
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+import { withHistory } from "../lib/history";
13
14
14class Point extends React.Component {
15class Point extends React.Component {
15 constructor(props) {
16 constructor(props) {
@@ -54,8 +55,8 @@ class Point extends React.Component {
54 .getTotalUsableInvites(ctrcs, point)
55 .getTotalUsableInvites(ctrcs, point)
55 .then(count => this.setState({ invites: Just(count) }));
56 .then(count => this.setState({ invites: Just(count) }));
56 this.updateSpawned(ctrcs, point);
57 this.updateSpawned(ctrcs, point);
57− })
58+ }),
58− )
59+ ),
59 );
60 );
60 }
61 }
61
62
@@ -73,14 +74,7 @@ class Point extends React.Component {
73 };
74 };
74
75
75 render() {
76 render() {
76− const {
77− web3,
78− popRoute,
79− pushRoute,
80− wallet,
81− setPointCursor,
77+ const { web3, history, wallet, setPointCursor, pointCache } = this.props;
82− pointCache,
83− } = this.props;
84
78
85 const { spawned } = this.state;
79 const { spawned } = this.state;
86
80
@@ -101,21 +95,16 @@ class Point extends React.Component {
101
95
102 const authenticated = Just.hasInstance(wallet);
96 const authenticated = Just.hasInstance(wallet);
103
97
104− const routeHandler = route => {
105− popRoute();
106− pushRoute(route);
107− };
108−
109 const issuedPointList =
98 const issuedPointList =
110 spawned.length === 0 ? (
99 spawned.length === 0 ? (
111 <div />
100 <div />
112 ) : (
101 ) : (
113 <div>
102 <div>
114− <H3>{'Issued Points'}</H3>
103+ <H3>{"Issued Points"}</H3>
115
104
116 <PointList
105 <PointList
117 setPointCursor={setPointCursor}
106 setPointCursor={setPointCursor}
118− routeHandler={routeHandler}
107+ routeHandler={history.popAndPush}
119 points={spawned}
108 points={spawned}
120 />
109 />
121 </div>
110 </div>
@@ -124,13 +113,12 @@ class Point extends React.Component {
124 return (
113 return (
125 <Row>
114 <Row>
126 <Col>
115 <Col>
127− <div className={'mt-12 pt-6'}>{sigil}</div>
116+ <div className={"mt-12 pt-6"}>{sigil}</div>
128 <H1>
117 <H1>
129 <code>{name}</code>
118 <code>{name}</code>
130 </H1>
119 </H1>
131 {authenticated ? (
120 {authenticated ? (
132 <Actions
121 <Actions
133− pushRoute={pushRoute}
134 online={online}
122 online={online}
135 wallet={wallet}
123 wallet={wallet}
136 point={point}
124 point={point}
@@ -147,4 +135,4 @@ class Point extends React.Component {
147 }
135 }
148}
136}
149
137
150−export default Point;
138+export default withHistory(Point);
src/bridge/views/Point/Actions.js+79−68
@@ -1,17 +1,20 @@
1−import React from 'react';
1+import React from "react";
2+import { azimuth } from "azimuth-js";
2−import * as need from '../../lib/need';
3+import * as need from "../../lib/need";
4+
3−import { ETH_ZERO_ADDR, CURVE_ZERO_ADDR, eqAddr } from '../../lib/wallet';
5+import { ETH_ZERO_ADDR, CURVE_ZERO_ADDR, eqAddr } from "../../lib/wallet";
4−import { Row, Col, H2, P } from '../../components/Base';
6+import { Row, Col, H2, P } from "../../components/Base";
5−import { Button } from '../../components/Base';
7+import { Button } from "../../components/Base";
6−import { ROUTE_NAMES } from '../../lib/router';
8+import { ROUTE_NAMES } from "../../lib/routeNames";
7−import { azimuth } from 'azimuth-js';
9+import { useHistory } from "../../lib/history";
8
10
9const isPlanet = point =>
11const isPlanet = point =>
10 azimuth.getPointSize(point) === azimuth.PointSize.Planet;
12 azimuth.getPointSize(point) === azimuth.PointSize.Planet;
11const isStar = point => azimuth.getPointSize(point) === azimuth.PointSize.Star;
13const isStar = point => azimuth.getPointSize(point) === azimuth.PointSize.Star;
12
14
13−const Actions = props => {
15+function Actions(props) {
16+ const history = useHistory();
14− const { pushRoute, online, point, pointDetails, invites } = props;
17+ const { online, point, pointDetails, invites } = props;
15
18
16 const addr = need.address(props);
19 const addr = need.address(props);
17
20
@@ -103,17 +106,16 @@ const Actions = props => {
103 inviteAction = (
106 inviteAction = (
104 <Button
107 <Button
105 disabled={!isActiveOwner || !online || !hasInvites}
108 disabled={!isActiveOwner || !online || !hasInvites}
106− prop-size={'sm'}
109+ prop-size={"sm"}
107− prop-type={'link'}
110+ prop-type={"link"}
108− onClick={() => {
109− pushRoute(ROUTE_NAMES.INVITES_SEND);
111+ onClick={() => history.push(ROUTE_NAMES.INVITES_SEND)}
110− }}>
112+ >
111− {'Send invites ('}
113+ {"Send invites ("}
112 {invites.matchWith({
114 {invites.matchWith({
113− Nothing: () => '?',
115+ Nothing: () => "?",
114 Just: count => count.value,
116 Just: count => count.value,
115 })}
117 })}
116− {')'}
118+ {")"}
117 </Button>
119 </Button>
118 );
120 );
119 }
121 }
@@ -121,114 +123,123 @@ const Actions = props => {
121 inviteAction = (
123 inviteAction = (
122 <Button
124 <Button
123 disabled={!(isActiveOwner && canIssueChild) || !online}
125 disabled={!(isActiveOwner && canIssueChild) || !online}
124− prop-size={'sm'}
126+ prop-size={"sm"}
125− prop-type={'link'}
127+ prop-type={"link"}
126 onClick={() => {
128 onClick={() => {
127− pushRoute(ROUTE_NAMES.INVITES_MANAGE);
129+ history.push(ROUTE_NAMES.INVITES_MANAGE);
128− }}>
130+ }}
131+ >
129− {'Manage invites'}
132+ {"Manage invites"}
130 </Button>
133 </Button>
131 );
134 );
132 }
135 }
133
136
134 return (
137 return (
135 <div>
138 <div>
136− <H2>{'Actions'}</H2>
139+ <H2>{"Actions"}</H2>
137 {displayReminder ? (
140 {displayReminder ? (
138 <P>{`Before you can issue child points or generate your Arvo
141 <P>{`Before you can issue child points or generate your Arvo
139 keyfile, you need to set your public keys.`}</P>
142 keyfile, you need to set your public keys.`}</P>
140 ) : (
143 ) : (
141− ''
144+ ""
142 )}
145 )}
143 <Row>
146 <Row>
144− <Col className={'flex flex-column items-start col-md-4'}>
147+ <Col className={"flex flex-column items-start col-md-4"}>
145 <Button
148 <Button
146− prop-size={'sm'}
149+ prop-size={"sm"}
147− prop-type={'link'}
150+ prop-type={"link"}
148 disabled={(online || planet) && !canIssueChild}
151 disabled={(online || planet) && !canIssueChild}
149 onClick={() => {
152 onClick={() => {
150− pushRoute(ROUTE_NAMES.ISSUE_CHILD);
153+ history.push(ROUTE_NAMES.ISSUE_CHILD);
151− }}>
154+ }}
155+ >
152− {'Issue child'}
156+ {"Issue child"}
153 </Button>
157 </Button>
154
158
155 <Button
159 <Button
156− prop-size={'sm'}
160+ prop-size={"sm"}
157− prop-type={'link'}
161+ prop-type={"link"}
158 disabled={online && !canAcceptTransfer}
162 disabled={online && !canAcceptTransfer}
159 onClick={() => {
163 onClick={() => {
160− pushRoute(ROUTE_NAMES.ACCEPT_TRANSFER);
164+ history.push(ROUTE_NAMES.ACCEPT_TRANSFER);
161− }}>
165+ }}
166+ >
162− {'Accept incoming transfer'}
167+ {"Accept incoming transfer"}
163 </Button>
168 </Button>
164
169
165 <Button
170 <Button
166− prop-size={'sm'}
171+ prop-size={"sm"}
167− prop-type={'link'}
172+ prop-type={"link"}
168 disabled={online && !canCancelTransfer}
173 disabled={online && !canCancelTransfer}
169 onClick={() => {
174 onClick={() => {
170− pushRoute(ROUTE_NAMES.CANCEL_TRANSFER);
175+ history.push(ROUTE_NAMES.CANCEL_TRANSFER);
171− }}>
176+ }}
177+ >
172− {'Cancel outgoing transfer'}
178+ {"Cancel outgoing transfer"}
173 </Button>
179 </Button>
174
180
175 <Button
181 <Button
176− prop-size={'sm'}
182+ prop-size={"sm"}
177− prop-type={'link'}
183+ prop-type={"link"}
178 disabled={online && !canGenKeyfile}
184 disabled={online && !canGenKeyfile}
179 onClick={() => {
185 onClick={() => {
180− pushRoute(ROUTE_NAMES.GEN_KEYFILE);
186+ history.push(ROUTE_NAMES.GEN_KEYFILE);
181− }}>
187+ }}
188+ >
182− {'Generate Arvo keyfile'}
189+ {"Generate Arvo keyfile"}
183 </Button>
190 </Button>
184 </Col>
191 </Col>
185− <Col className={'flex flex-column items-start col-md-4'}>
192+ <Col className={"flex flex-column items-start col-md-4"}>
186 <Button
193 <Button
187 disabled={online && !canSetSpawnProxy}
194 disabled={online && !canSetSpawnProxy}
188− prop-size={'sm'}
195+ prop-size={"sm"}
189− prop-type={'link'}
196+ prop-type={"link"}
190 onClick={() => {
197 onClick={() => {
191− pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY);
198+ history.push(ROUTE_NAMES.SET_SPAWN_PROXY);
192− }}>
199+ }}
200+ >
193− {'Change spawn proxy'}
201+ {"Change spawn proxy"}
194 </Button>
202 </Button>
195
203
196 <Button
204 <Button
197 disabled={online && !canSetManagementProxy}
205 disabled={online && !canSetManagementProxy}
198− prop-size={'sm'}
206+ prop-size={"sm"}
199− prop-type={'link'}
207+ prop-type={"link"}
200 onClick={() => {
208 onClick={() => {
201− pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY);
209+ history.push(ROUTE_NAMES.SET_MANAGEMENT_PROXY);
202− }}>
210+ }}
211+ >
203− {'Change management proxy'}
212+ {"Change management proxy"}
204 </Button>
213 </Button>
205
214
206 <Button
215 <Button
207 disabled={online && !canConfigureKeys}
216 disabled={online && !canConfigureKeys}
208− prop-size={'sm'}
217+ prop-size={"sm"}
209− prop-type={'link'}
218+ prop-type={"link"}
210 onClick={() => {
219 onClick={() => {
211− pushRoute(ROUTE_NAMES.SET_KEYS);
220+ history.push(ROUTE_NAMES.SET_KEYS);
212− }}>
221+ }}
222+ >
213− {'Set network keys'}
223+ {"Set network keys"}
214 </Button>
224 </Button>
215
225
216 <Button
226 <Button
217 disabled={online && !canTransfer}
227 disabled={online && !canTransfer}
218− prop-size={'sm'}
228+ prop-size={"sm"}
219− prop-type={'link'}
229+ prop-type={"link"}
220 onClick={() => {
230 onClick={() => {
221− pushRoute(ROUTE_NAMES.TRANSFER);
231+ history.push(ROUTE_NAMES.TRANSFER);
222− }}>
232+ }}
233+ >
223− {'Transfer'}
234+ {"Transfer"}
224 </Button>
235 </Button>
225 </Col>
236 </Col>
226− <Col className={'flex flex-column items-start col-md-4'}>
237+ <Col className={"flex flex-column items-start col-md-4"}>
227 {inviteAction}
238 {inviteAction}
228 </Col>
239 </Col>
229 </Row>
240 </Row>
230 </div>
241 </div>
231 );
242 );
232−};
243+}
233
244
234export default Actions;
245export default Actions;
src/bridge/views/Points.js+40−37
@@ -1,12 +1,13 @@
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/routeNames";
9+import { withHistory } from "../lib/history";
9−import { ETH_ZERO_ADDR, eqAddr } from '../lib/wallet';
10+import { ETH_ZERO_ADDR, eqAddr } from "../lib/wallet";
10
11
11const hasTransferProxy = (cache, point) =>
12const hasTransferProxy = (cache, point) =>
12 point in cache ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR) : false;
13 point in cache ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR) : false;
@@ -41,7 +42,7 @@ class Points extends React.Component {
41
42
42 const incoming = await azimuth.azimuth.getTransferringFor(
43 const incoming = await azimuth.azimuth.getTransferringFor(
43 ctrcs,
44 ctrcs,
44− addr
45+ addr,
45 );
46 );
46
47
47 const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
48 const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
@@ -65,8 +66,8 @@ class Points extends React.Component {
65 });
66 });
66
67
67 this.cachePoints(ctrcs, addToPointCache, points);
68 this.cachePoints(ctrcs, addToPointCache, points);
68− })
69+ }),
69− )
70+ ),
70 );
71 );
71 }
72 }
72
73
@@ -79,7 +80,7 @@ class Points extends React.Component {
79 }
80 }
80
81
81 render() {
82 render() {
82− const { setPointCursor, pushRoute } = this.props;
83+ const { setPointCursor, history } = this.props;
83 const { pointCache } = this.props;
84 const { pointCache } = this.props;
84
85
85 const {
86 const {
@@ -96,12 +97,13 @@ class Points extends React.Component {
96 <Row>
97 <Row>
97 <Col>
98 <Col>
98 <Button
99 <Button
99− prop-type={'link'}
100+ prop-type={"link"}
100− prop-size={'md'}
101+ prop-size={"md"}
101− onClick={() => pushRoute(ROUTE_NAMES.VIEW_SHIP)}>
102+ onClick={() => history.push(ROUTE_NAMES.VIEW_SHIP)}
103+ >
102− {'View a point →'}
104+ {"View a point →"}
103 </Button>
105 </Button>
104− <P>{'View a point on Azimuth.'}</P>
106+ <P>{"View a point on Azimuth."}</P>
105 </Col>
107 </Col>
106 </Row>
108 </Row>
107 );
109 );
@@ -113,14 +115,15 @@ class Points extends React.Component {
113 <Row>
115 <Row>
114 <Col>
116 <Col>
115 <Button
117 <Button
116− prop-type={'link'}
118+ prop-type={"link"}
117− prop-size={'md'}
119+ prop-size={"md"}
118− onClick={() => pushRoute(ROUTE_NAMES.CREATE_GALAXY)}>
120+ onClick={() => history.push(ROUTE_NAMES.CREATE_GALAXY)}
121+ >
119− {'Create a galaxy →'}
122+ {"Create a galaxy →"}
120 </Button>
123 </Button>
121 <P>
124 <P>
122 {
125 {
123− 'You have the authority to create a new Galaxy and you can do so here.'
126+ "You have the authority to create a new Galaxy and you can do so here."
124 }
127 }
125 </P>
128 </P>
126 </Col>
129 </Col>
@@ -128,20 +131,20 @@ class Points extends React.Component {
128 );
131 );
129
132
130 const outgoing = points.filter(point =>
133 const outgoing = points.filter(point =>
131− hasTransferProxy(pointCache, point)
134+ hasTransferProxy(pointCache, point),
132 );
135 );
133
136
134 const outgoingPoints =
137 const outgoingPoints =
135 outgoing.length !== 0 ? (
138 outgoing.length !== 0 ? (
136 <React.Fragment>
139 <React.Fragment>
137− <H2>{'Outgoing Transfers'}</H2>
140+ <H2>{"Outgoing Transfers"}</H2>
138 <P>
141 <P>
139 {`You own these points until the recipient accepts the incoming
142 {`You own these points until the recipient accepts the incoming
140 transfer. You may cancel the transfer until accepted.`}
143 transfer. You may cancel the transfer until accepted.`}
141 </P>
144 </P>
142 <PointList
145 <PointList
143 setPointCursor={setPointCursor}
146 setPointCursor={setPointCursor}
144− routeHandler={pushRoute}
147+ routeHandler={history.push}
145 points={outgoing}
148 points={outgoing}
146 />
149 />
147 </React.Fragment>
150 </React.Fragment>
@@ -152,14 +155,14 @@ class Points extends React.Component {
152 const incomingPoints =
155 const incomingPoints =
153 incoming.length !== 0 ? (
156 incoming.length !== 0 ? (
154 <React.Fragment>
157 <React.Fragment>
155− <H2>{'Incoming Transfers'}</H2>
158+ <H2>{"Incoming Transfers"}</H2>
156 <P>
159 <P>
157 {`You do not own these points until you accept the incoming transfer.
160 {`You do not own these points until you accept the incoming transfer.
158 You may reject any incoming transfers.`}
161 You may reject any incoming transfers.`}
159 </P>
162 </P>
160 <PointList
163 <PointList
161 setPointCursor={setPointCursor}
164 setPointCursor={setPointCursor}
162− routeHandler={pushRoute}
165+ routeHandler={history.push}
163 points={incoming}
166 points={incoming}
164 />
167 />
165 </React.Fragment>
168 </React.Fragment>
@@ -170,14 +173,14 @@ class Points extends React.Component {
170 const managingPoints =
173 const managingPoints =
171 managing.length !== 0 ? (
174 managing.length !== 0 ? (
172 <React.Fragment>
175 <React.Fragment>
173− <H2>{'You Are a Management Proxy For'}</H2>
176+ <H2>{"You Are a Management Proxy For"}</H2>
174 <P>
177 <P>
175 {`You can configure or set network keys and conduct sponsorship
178 {`You can configure or set network keys and conduct sponsorship
176 related operations for these points.`}
179 related operations for these points.`}
177 </P>
180 </P>
178 <PointList
181 <PointList
179 setPointCursor={setPointCursor}
182 setPointCursor={setPointCursor}
180− routeHandler={pushRoute}
183+ routeHandler={history.push}
181 points={managing}
184 points={managing}
182 />
185 />
183 </React.Fragment>
186 </React.Fragment>
@@ -188,14 +191,14 @@ class Points extends React.Component {
188 const votingPoints =
191 const votingPoints =
189 voting.length !== 0 ? (
192 voting.length !== 0 ? (
190 <React.Fragment>
193 <React.Fragment>
191− <H2>{'You Are a Voting Proxy For'}</H2>
194+ <H2>{"You Are a Voting Proxy For"}</H2>
192 <P>
195 <P>
193 {`Since you are part of the Galactic Senate, you can cast votes on
196 {`Since you are part of the Galactic Senate, you can cast votes on
194 new Azimuth proposals on behalf of these points.`}
197 new Azimuth proposals on behalf of these points.`}
195 </P>
198 </P>
196 <PointList
199 <PointList
197 setPointCursor={setPointCursor}
200 setPointCursor={setPointCursor}
198− routeHandler={pushRoute}
201+ routeHandler={history.push}
199 points={voting}
202 points={voting}
200 />
203 />
201 </React.Fragment>
204 </React.Fragment>
@@ -206,11 +209,11 @@ class Points extends React.Component {
206 const spawningPoints =
209 const spawningPoints =
207 spawning.length !== 0 ? (
210 spawning.length !== 0 ? (
208 <React.Fragment>
211 <React.Fragment>
209− <H2>{'You Are a Spawn Proxy For'}</H2>
212+ <H2>{"You Are a Spawn Proxy For"}</H2>
210 <P>{`You can create new child ships under these points.`}</P>
213 <P>{`You can create new child ships under these points.`}</P>
211 <PointList
214 <PointList
212 setPointCursor={setPointCursor}
215 setPointCursor={setPointCursor}
213− routeHandler={pushRoute}
216+ routeHandler={history.push}
214 points={spawning}
217 points={spawning}
215 />
218 />
216 </React.Fragment>
219 </React.Fragment>
@@ -221,7 +224,7 @@ class Points extends React.Component {
221 return (
224 return (
222 <Row>
225 <Row>
223 <Col>
226 <Col>
224− <H1>{'Points'}</H1>
227+ <H1>{"Points"}</H1>
225
228
226 <P>{`A point is an identity on the Ethereum blockchain.
229 <P>{`A point is an identity on the Ethereum blockchain.
227 Points declare keys for ships on the Arvo network.`}</P>
230 Points declare keys for ships on the Arvo network.`}</P>
@@ -234,11 +237,11 @@ class Points extends React.Component {
234
237
235 {outgoingPoints}
238 {outgoingPoints}
236
239
237− <H2>{'Your Points'}</H2>
240+ <H2>{"Your Points"}</H2>
238
241
239 <PointList
242 <PointList
240 setPointCursor={setPointCursor}
243 setPointCursor={setPointCursor}
241− routeHandler={pushRoute}
244+ routeHandler={history.push}
242 points={points}
245 points={points}
243 loading={loading}
246 loading={loading}
244 />
247 />
@@ -254,4 +257,4 @@ class Points extends React.Component {
254 }
257 }
255}
258}
256
259
257−export default Points;
260+export default withHistory(Points);
src/bridge/views/PrivateKey.js+22−22
@@ -1,18 +1,19 @@
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/routeNames";
8+import { withHistory } from "../lib/history";
8−import { EthereumWallet } from '../lib/wallet';
9+import { EthereumWallet } from "../lib/wallet";
9
10
10class PrivateKey extends React.Component {
11class PrivateKey extends React.Component {
11 constructor(props) {
12 constructor(props) {
12 super(props);
13 super(props);
13
14
14 this.state = {
15 this.state = {
15− privateKey: '',
16+ privateKey: "",
16 };
17 };
17
18
18 this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this);
19 this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this);
@@ -26,7 +27,7 @@ class PrivateKey extends React.Component {
26 constructWallet(privateKey) {
27 constructWallet(privateKey) {
27 const { setWallet } = this.props;
28 const { setWallet } = this.props;
28 if (/^[0-9A-Fa-f]{64}$/g.test(privateKey) === true) {
29 if (/^[0-9A-Fa-f]{64}$/g.test(privateKey) === true) {
29− const sec = Buffer.from(privateKey, 'hex');
30+ const sec = Buffer.from(privateKey, "hex");
30 const wallet = new EthereumWallet(sec);
31 const wallet = new EthereumWallet(sec);
31 setWallet(Maybe.Just(wallet));
32 setWallet(Maybe.Just(wallet));
32 } else {
33 } else {
@@ -35,13 +36,13 @@ class PrivateKey extends React.Component {
35 }
36 }
36
37
37 render() {
38 render() {
38− const { pushRoute, popRoute, wallet } = this.props;
39+ const { history, wallet } = this.props;
39 const { privateKey } = this.state;
40 const { privateKey } = this.state;
40
41
41 return (
42 return (
42 <Row>
43 <Row>
43− <Col className={'measure-md'}>
44+ <Col className={"measure-md"}>
44− <H1 className={'mb-4'}>{'Enter Your Private Key'}</H1>
45+ <H1 className={"mb-4"}>{"Enter Your Private Key"}</H1>
45 <InputCaption>
46 <InputCaption>
46 {`Please enter your raw Ethereum private key here.`}
47 {`Please enter your raw Ethereum private key here.`}
47 </InputCaption>
48 </InputCaption>
@@ -55,19 +56,18 @@ class PrivateKey extends React.Component {
55 onChange={this.handlePrivateKeyInput}
56 onChange={this.handlePrivateKeyInput}
56 value={privateKey}
57 value={privateKey}
57 autocomplete="off"
58 autocomplete="off"
58− autoFocus>
59+ autoFocus
60+ >
59− <InnerLabel>{'Private Key'}</InnerLabel>
61+ <InnerLabel>{"Private Key"}</InnerLabel>
60 </RequiredInput>
62 </RequiredInput>
61
63
62 <Button
64 <Button
63− className={'mt-10'}
65+ className={"mt-10"}
64− prop-size={'wide lg'}
66+ prop-size={"wide lg"}
65 disabled={Maybe.Nothing.hasInstance(wallet)}
67 disabled={Maybe.Nothing.hasInstance(wallet)}
66− onClick={() => {
67− popRoute();
68− pushRoute(ROUTE_NAMES.SHIPS);
68+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
69− }}>
69+ >
70− {'Continue →'}
70+ {"Continue →"}
71 </Button>
71 </Button>
72 </Col>
72 </Col>
73 </Row>
73 </Row>
@@ -75,4 +75,4 @@ class PrivateKey extends React.Component {
75 }
75 }
76}
76}
77
77
78−export default PrivateKey;
78+export default withHistory(PrivateKey);
src/bridge/views/SentTransaction.js+42−45
@@ -1,25 +1,27 @@
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−
6+
7−import { ROUTE_NAMES } from '../lib/router';
7+import { ROUTE_NAMES } from "../lib/routeNames";
8+import { useHistory } from "../lib/history";
8−import { BRIDGE_ERROR, renderTxnError } from '../lib/error';
9+import { BRIDGE_ERROR, renderTxnError } from "../lib/error";
9−import { NETWORK_NAMES } from '../lib/network';
10+import { NETWORK_NAMES } from "../lib/network";
11+import { withTxnConfirmations } from "../store/txnConfirmations";
10
12
11class Success extends React.Component {
13class Success extends React.Component {
12 constructor(props) {
14 constructor(props) {
13 super(props);
15 super(props);
14
16
15 this.state = {
17 this.state = {
16− pending: '.',
18+ pending: ".",
17 interval: null,
19 interval: null,
18 };
20 };
19 }
21 }
20
22
21 componentDidMount() {
23 componentDidMount() {
22− const nextDot = { '.': '..', '..': '...', '...': '.' };
24+ const nextDot = { ".": "..", "..": "...", "...": "." };
23
25
24 const interval = setInterval(() => {
26 const interval = setInterval(() => {
25 this.setState(({ pending }) => ({ pending: nextDot[pending] }));
27 this.setState(({ pending }) => ({ pending: nextDot[pending] }));
@@ -42,27 +44,28 @@ class Success extends React.Component {
42
44
43 const esdomain =
45 const esdomain =
44 networkType === NETWORK_NAMES.ROPSTEN
46 networkType === NETWORK_NAMES.ROPSTEN
45− ? 'ropsten.etherscan.io'
47+ ? "ropsten.etherscan.io"
46− : 'etherscan.io';
48+ : "etherscan.io";
47
49
48 const esmessage =
50 const esmessage =
49 esvisible === true
51 esvisible === true
50− ? 'If you’d like to keep track of it, click the Etherscan link below.'
52+ ? "If you’d like to keep track of it, click the Etherscan link below."
51− : '';
53+ : "";
52
54
53 const esanchor =
55 const esanchor =
54 esvisible === false ? null : (
56 esvisible === false ? null : (
55 <Anchor
57 <Anchor
56− className={'mb-4 mt-1'}
58+ className={"mb-4 mt-1"}
57− prop-size={'sm'}
59+ prop-size={"sm"}
58− target={'_blank'}
60+ target={"_blank"}
59− href={`https://${esdomain}/tx/${hash}`}>
61+ href={`https://${esdomain}/tx/${hash}`}
62+ >
60− {'View on Etherscan ↗'}
63+ {"View on Etherscan ↗"}
61 </Anchor>
64 </Anchor>
62 );
65 );
63
66
64 const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(
67 const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(
65− 0
68+ 0,
66 );
69 );
67
70
68 const requiredConfirmations = 1;
71 const requiredConfirmations = 1;
@@ -75,17 +78,17 @@ class Success extends React.Component {
75 return (
78 return (
76 <Row>
79 <Row>
77 <Col>
80 <Col>
78− <H1>{'Your Transaction was Sent'}</H1>
81+ <H1>{"Your Transaction was Sent"}</H1>
79
82
80 <P>
83 <P>
81 {`We sent your transaction to the chain. It can take some time to
84 {`We sent your transaction to the chain. It can take some time to
82 execute, especially if the network is busy. ${esmessage}`}
85 execute, especially if the network is busy. ${esmessage}`}
83 </P>
86 </P>
84
87
85− <H3>{'Transaction Hash'}</H3>
88+ <H3>{"Transaction Hash"}</H3>
86 <P>{hash}</P>
89 <P>{hash}</P>
87
90
88− <H3>{'Transaction Status'}</H3>
91+ <H3>{"Transaction Status"}</H3>
89 <P>{status}</P>
92 <P>{status}</P>
90 <P>{esanchor}</P>
93 <P>{esanchor}</P>
91 </Col>
94 </Col>
@@ -97,10 +100,10 @@ class Success extends React.Component {
97const Failure = props => (
100const Failure = props => (
98 <Row>
101 <Row>
99 <Col>
102 <Col>
100− <H1>{'Error!'}</H1>
103+ <H1>{"Error!"}</H1>
101
104
102 <Warning>
105 <Warning>
103− <H3>{'There was an error sending your transaction.'}</H3>
106+ <H3>{"There was an error sending your transaction."}</H3>
104 {renderTxnError(props.web3, props.message)}
107 {renderTxnError(props.web3, props.message)}
105 </Warning>
108 </Warning>
106 </Col>
109 </Col>
@@ -108,16 +111,11 @@ const Failure = props => (
108);
111);
109
112
110const SentTransaction = props => {
113const SentTransaction = props => {
111− const {
114+ const history = useHistory();
112− txnHashCursor,
113− networkType,
114− popRoute,
115− pushRoute,
116− txnConfirmations,
115+ const { txnHashCursor, networkType, txnConfirmations } = props;
117− } = props;
118 const { setPointCursor, pointCursor } = props;
116 const { setPointCursor, pointCursor } = props;
119
117
120− const promptKeyfile = props.routeData && props.routeData.promptKeyfile;
118+ const promptKeyfile = history.data() && history.data().promptKeyfile;
121
119
122 const w3 = need.web3(props);
120 const w3 = need.web3(props);
123
121
@@ -143,12 +141,13 @@ const SentTransaction = props => {
143 <Row>
141 <Row>
144 <Col>
142 <Col>
145 <Button
143 <Button
146− prop-type={'link'}
144+ prop-type={"link"}
147 onClick={() => {
145 onClick={() => {
148 setPointCursor(pointCursor);
146 setPointCursor(pointCursor);
149− popRoute();
147+ history.pop();
150− }}>
148+ }}
149+ >
151− {'Ok →'}
150+ {"Ok →"}
152 </Button>
151 </Button>
153 </Col>
152 </Col>
154 </Row>
153 </Row>
@@ -161,12 +160,10 @@ const SentTransaction = props => {
161 <Row>
160 <Row>
162 <Col>
161 <Col>
163 <Button
162 <Button
164− prop-type={'link'}
163+ prop-type={"link"}
165− onClick={() => {
166− popRoute();
167− pushRoute(ROUTE_NAMES.GEN_KEYFILE);
164+ onClick={() => history.popAndPush(ROUTE_NAMES.GEN_KEYFILE)}
168− }}>
165+ >
169− {'Download Keyfile →'}
166+ {"Download Keyfile →"}
170 </Button>
167 </Button>
171 </Col>
168 </Col>
172 </Row>
169 </Row>
@@ -182,4 +179,4 @@ const SentTransaction = props => {
182 );
179 );
183};
180};
184
181
185−export default SentTransaction;
182+export default withTxnConfirmations(SentTransaction);
src/bridge/views/SetKeys.js+36−36
@@ -1,16 +1,16 @@
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 "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) {
@@ -19,9 +19,9 @@ class SetKeys extends React.Component {
19 const point = need.pointCursor(props);
19 const point = need.pointCursor(props);
20
20
21 this.state = {
21 this.state = {
22− auth: '',
22+ auth: "",
23− encr: '',
23+ encr: "",
24− networkSeed: '',
24+ networkSeed: "",
25 nondeterministicSeed: false,
25 nondeterministicSeed: false,
26 point: point,
26 point: point,
27 cryptoSuiteVersion: 1,
27 cryptoSuiteVersion: 1,
@@ -64,22 +64,22 @@ class SetKeys extends React.Component {
64 hex =
64 hex =
65 hex +
65 hex +
66 [
66 [
67− '0',
67+ "0",
68− '1',
68+ "1",
69− '2',
69+ "2",
70− '3',
70+ "3",
71− '4',
71+ "4",
72− '5',
72+ "5",
73− '6',
73+ "6",
74− '7',
74+ "7",
75− '8',
75+ "8",
76− '9',
76+ "9",
77− 'A',
77+ "A",
78− 'B',
78+ "B",
79− 'C',
79+ "C",
80− 'D',
80+ "D",
81− 'E',
81+ "E",
82− 'F',
82+ "F",
83 ][Math.floor(Math.random() * 16)];
83 ][Math.floor(Math.random() * 16)];
84 }
84 }
85
85
@@ -91,13 +91,13 @@ class SetKeys extends React.Component {
91 let seed = await attemptSeedDerivation(next, this.props);
91 let seed = await attemptSeedDerivation(next, this.props);
92 let nondeterministicSeed = false;
92 let nondeterministicSeed = false;
93
93
94− if (seed.getOrElse('') === '') {
94+ if (seed.getOrElse("") === "") {
95 seed = Maybe.Just(this.randomHex(64));
95 seed = Maybe.Just(this.randomHex(64));
96 nondeterministicSeed = true;
96 nondeterministicSeed = true;
97 }
97 }
98
98
99 this.setState({
99 this.setState({
100− networkSeed: seed.getOrElse(''),
100+ networkSeed: seed.getOrElse(""),
101 nondeterministicSeed: nondeterministicSeed,
101 nondeterministicSeed: nondeterministicSeed,
102 });
102 });
103 }
103 }
@@ -124,7 +124,7 @@ class SetKeys extends React.Component {
124 pencr,
124 pencr,
125 pauth,
125 pauth,
126 1,
126 1,
127− state.discontinuity
127+ state.discontinuity,
128 );
128 );
129
129
130 return Maybe.Just(txn);
130 return Maybe.Just(txn);
@@ -145,7 +145,7 @@ class SetKeys extends React.Component {
145 <Row>
145 <Row>
146 <Col>
146 <Col>
147 <H1>
147 <H1>
148− {'Set Network Keys For '} <code>{`${ob.patp(state.point)}`}</code>
148+ {"Set Network Keys For "} <code>{`${ob.patp(state.point)}`}</code>
149 </H1>
149 </H1>
150
150
151 <P className="mt-10">
151 <P className="mt-10">
@@ -154,7 +154,7 @@ class SetKeys extends React.Component {
154
154
155 {state.nondeterministicSeed && (
155 {state.nondeterministicSeed && (
156 <Warning>
156 <Warning>
157− <h3 className={'mb-2'}>{'Warning'}</h3>
157+ <h3 className={"mb-2"}>{"Warning"}</h3>
158 {`Your network seed could not be derived automatically. We've
158 {`Your network seed could not be derived automatically. We've
159 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
160 keyfile during this session after setting your keys.`}
160 keyfile during this session after setting your keys.`}
@@ -163,8 +163,8 @@ class SetKeys extends React.Component {
163
163
164 {pointDetails.keyRevisionNumber === 0 ? (
164 {pointDetails.keyRevisionNumber === 0 ? (
165 <Warning>
165 <Warning>
166− <h3 className={'mb-2'}>{'Warning'}</h3>
166+ <h3 className={"mb-2"}>{"Warning"}</h3>
167− {'Once these keys have been set, your point is considered ' +
167+ {"Once these keys have been set, your point is considered " +
168 "'linked'. This operation cannot be undone."}
168 "'linked'. This operation cannot be undone."}
169 </Warning>
169 </Warning>
170 ) : (
170 ) : (
src/bridge/views/SetProxy.js+31−29
@@ -1,7 +1,7 @@
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";
5import {
5import {
6 Row,
6 Row,
7 Col,
7 Col,
@@ -11,17 +11,17 @@ import {
11 ShowBlockie,
11 ShowBlockie,
12 Anchor,
12 Anchor,
13 HorizontalSelector,
13 HorizontalSelector,
14−} from '../components/Base';
14+} from "../components/Base";
15−import { AddressInput } from '../components/Base';
15+import { AddressInput } from "../components/Base";
16−import * as ob from 'urbit-ob';
16+import * as ob from "urbit-ob";
17
17
18−import { PROXY_TYPE, renderProxyType } from '../lib/proxy';
18+import { PROXY_TYPE, renderProxyType } from "../lib/proxy";
19
19
20−import { NETWORK_NAMES } from '../lib/network';
20+import { NETWORK_NAMES } from "../lib/network";
21
21
22−import StatelessTransaction from '../components/StatelessTransaction';
22+import StatelessTransaction from "../components/StatelessTransaction";
23
23
24−import { isValidAddress } from '../lib/wallet';
24+import { isValidAddress } from "../lib/wallet";
25
25
26const SetManagementProxy = props => (
26const SetManagementProxy = props => (
27 <SetProxy {...props} proxyType={PROXY_TYPE.MANAGEMENT_PROXY} />
27 <SetProxy {...props} proxyType={PROXY_TYPE.MANAGEMENT_PROXY} />
@@ -42,7 +42,7 @@ class SetProxy extends React.Component {
42 const issuingPoint = need.pointCursor(props);
42 const issuingPoint = need.pointCursor(props);
43
43
44 this.state = {
44 this.state = {
45− proxyAddress: '',
45+ proxyAddress: "",
46 issuingPoint: issuingPoint,
46 issuingPoint: issuingPoint,
47 setProxy: true,
47 setProxy: true,
48 };
48 };
@@ -54,10 +54,10 @@ class SetProxy extends React.Component {
54 }
54 }
55
55
56 handleSetUnset(selected) {
56 handleSetUnset(selected) {
57− const set = selected === 'set';
57+ const set = selected === "set";
58 this.setState({
58 this.setState({
59 setProxy: set,
59 setProxy: set,
60− proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000',
60+ proxyAddress: set ? "" : "0x0000000000000000000000000000000000000000",
61 });
61 });
62 }
62 }
63
63
@@ -104,26 +104,26 @@ class SetProxy extends React.Component {
104
104
105 const esdomain =
105 const esdomain =
106 props.networkType === NETWORK_NAMES.ROPSTEN
106 props.networkType === NETWORK_NAMES.ROPSTEN
107− ? 'ropsten.etherscan.io'
107+ ? "ropsten.etherscan.io"
108− : 'etherscan.io';
108+ : "etherscan.io";
109
109
110 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
110 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
111
111
112 const setUnset = [
112 const setUnset = [
113− { title: 'Set', value: 'set' },
113+ { title: "Set", value: "set" },
114− { title: 'Unset', value: 'unset' },
114+ { title: "Unset", value: "unset" },
115 ];
115 ];
116
116
117− const titleVerb = this.state.setProxy ? 'Set' : 'Unset';
117+ const titleVerb = this.state.setProxy ? "Set" : "Unset";
118
118
119 let addressInput = null;
119 let addressInput = null;
120 if (this.state.setProxy) {
120 if (this.state.setProxy) {
121 addressInput = (
121 addressInput = (
122 <>
122 <>
123 <P className="mt-8">
123 <P className="mt-8">
124− {'Please provide an Ethereum address to act as the ' +
124+ {"Please provide an Ethereum address to act as the " +
125 `${renderedProxyType} proxy. You can also use Wallet ` +
125 `${renderedProxyType} proxy. You can also use Wallet ` +
126− 'Generator to generate a keypair.'}
126+ "Generator to generate a keypair."}
127 </P>
127 </P>
128
128
129 <AddressInput
129 <AddressInput
@@ -132,18 +132,20 @@ class SetProxy extends React.Component {
132 prop-format="innerLabel"
132 prop-format="innerLabel"
133 placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
133 placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
134 value={state.proxyAddress}
134 value={state.proxyAddress}
135− onChange={v => this.handleAddressInput(v)}>
135+ onChange={v => this.handleAddressInput(v)}
136+ >
136− <InnerLabel>{'Proxy Address'}</InnerLabel>
137+ <InnerLabel>{"Proxy Address"}</InnerLabel>
137− <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
138+ <ShowBlockie className={"mt-1"} address={state.proxyAddress} />
138 </AddressInput>
139 </AddressInput>
139
140
140 <Anchor
141 <Anchor
141− className={'mt-1'}
142+ className={"mt-1"}
142− prop-size={'sm'}
143+ prop-size={"sm"}
143 prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
144 prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
144− target={'_blank'}
145+ target={"_blank"}
145− href={`https://${esdomain}/address/${state.proxyAddress}`}>
146+ href={`https://${esdomain}/address/${state.proxyAddress}`}
147+ >
146− {'View on Etherscan ↗'}
148+ {"View on Etherscan ↗"}
147 </Anchor>
149 </Anchor>
148 </>
150 </>
149 );
151 );
src/bridge/views/Shards.js+55−50
@@ -1,6 +1,6 @@
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,
@@ -8,27 +8,28 @@ import {
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 "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/routeNames";
17+import { withHistory } from "../lib/history";
17−import { urbitWalletFromTicket } from '../lib/wallet';
18+import { urbitWalletFromTicket } from "../lib/wallet";
18
19
19const placeholder = len => {
20const placeholder = len => {
20 let bytes = window.crypto.getRandomValues(new Uint8Array(len));
21 let bytes = window.crypto.getRandomValues(new Uint8Array(len));
21 let hex = bytes.reduce(
22 let hex = bytes.reduce(
22− (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
23+ (acc, byt) => acc + byt.toString(16).padStart(2, "0"),
23− ''
24+ "",
24 );
25 );
25 return ob.hex2patq(hex);
26 return ob.hex2patq(hex);
26};
27};
27
28
28const SHARDS = {
29const SHARDS = {
29− SHARD1: Symbol('SHARD1'),
30+ SHARD1: Symbol("SHARD1"),
30− SHARD2: Symbol('SHARD2'),
31+ SHARD2: Symbol("SHARD2"),
31− SHARD3: Symbol('SHARD3'),
32+ SHARD3: Symbol("SHARD3"),
32};
33};
33
34
34class Shards extends React.Component {
35class Shards extends React.Component {
@@ -36,11 +37,11 @@ class Shards extends React.Component {
36 super(props);
37 super(props);
37
38
38 this.state = {
39 this.state = {
39− shard1: '',
40+ shard1: "",
40− shard2: '',
41+ shard2: "",
41− shard3: '',
42+ shard3: "",
42− passphrase: '',
43+ passphrase: "",
43− pointName: '',
44+ pointName: "",
44 };
45 };
45
46
46 this.pointPlaceholder = placeholder(4);
47 this.pointPlaceholder = placeholder(4);
@@ -72,9 +73,9 @@ class Shards extends React.Component {
72 }
73 }
73
74
74 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
75 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
75− const s1 = shard1 === '' ? undefined : shard1;
76+ const s1 = shard1 === "" ? undefined : shard1;
76− const s2 = shard2 === '' ? undefined : shard2;
77+ const s2 = shard2 === "" ? undefined : shard2;
77− const s3 = shard3 === '' ? undefined : shard3;
78+ const s3 = shard3 === "" ? undefined : shard3;
78
79
79 let ticket = undefined;
80 let ticket = undefined;
80 try {
81 try {
@@ -90,19 +91,19 @@ class Shards extends React.Component {
90 }
91 }
91
92
92 render() {
93 render() {
93− const { popRoute, pushRoute, wallet } = this.props;
94+ const { history, wallet } = this.props;
94 const { shard1, shard2, shard3, pointName, passphrase } = this.state;
95 const { shard1, shard2, shard3, pointName, passphrase } = this.state;
95
96
96 const phPoint = this.pointPlaceholder;
97 const phPoint = this.pointPlaceholder;
97 const phTick = this.ticketPlaceholder;
98 const phTick = this.ticketPlaceholder;
98
99
99 const shards = [shard1, shard2, shard3];
100 const shards = [shard1, shard2, shard3];
100− const ready = shards.filter(x => x !== '').length > 1;
101+ const ready = shards.filter(x => x !== "").length > 1;
101
102
102 return (
103 return (
103 <Row>
104 <Row>
104 <Col>
105 <Col>
105− <H1>{'Authenticate'}</H1>
106+ <H1>{"Authenticate"}</H1>
106
107
107 <P>
108 <P>
108 {`Enter your point and at least two of your three Urbit master
109 {`Enter your point and at least two of your three Urbit master
@@ -118,10 +119,11 @@ class Shards extends React.Component {
118 autoFocus
119 autoFocus
119 placeholder={`e.g. ${phPoint}`}
120 placeholder={`e.g. ${phPoint}`}
120 value={pointName}
121 value={pointName}
121− onChange={this.handlePointNameInput}>
122+ onChange={this.handlePointNameInput}
123+ >
122− <InnerLabel>{'Point'}</InnerLabel>
124+ <InnerLabel>{"Point"}</InnerLabel>
123 <ValidatedSigil
125 <ValidatedSigil
124− className={'tr-0 mt-05 mr-0 abs'}
126+ className={"tr-0 mt-05 mr-0 abs"}
125 patp={pointName}
127 patp={pointName}
126 size={68}
128 size={68}
127 margin={8}
129 margin={8}
@@ -136,8 +138,9 @@ class Shards extends React.Component {
136 name="shard1"
138 name="shard1"
137 placeholder={`e.g. ${phTick}`}
139 placeholder={`e.g. ${phTick}`}
138 value={shard1}
140 value={shard1}
139− onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
141+ onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}
142+ >
140− <InnerLabel>{'Shard 1'}</InnerLabel>
143+ <InnerLabel>{"Shard 1"}</InnerLabel>
141 </ShardInput>
144 </ShardInput>
142
145
143 <ShardInput
146 <ShardInput
@@ -148,8 +151,9 @@ class Shards extends React.Component {
148 name="shard2"
151 name="shard2"
149 placeholder={`e.g. ${phTick}`}
152 placeholder={`e.g. ${phTick}`}
150 value={shard2}
153 value={shard2}
151− onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
154+ onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}
155+ >
152− <InnerLabel>{'Shard 2'}</InnerLabel>
156+ <InnerLabel>{"Shard 2"}</InnerLabel>
153 </ShardInput>
157 </ShardInput>
154
158
155 <ShardInput
159 <ShardInput
@@ -160,12 +164,13 @@ class Shards extends React.Component {
160 name="shard3"
164 name="shard3"
161 placeholder={`e.g. ${phTick}`}
165 placeholder={`e.g. ${phTick}`}
162 value={shard3}
166 value={shard3}
163− onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
167+ onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}
168+ >
164− <InnerLabel>{'Shard 3'}</InnerLabel>
169+ <InnerLabel>{"Shard 3"}</InnerLabel>
165 </ShardInput>
170 </ShardInput>
166
171
167 <InputCaption>
172 <InputCaption>
168− {'If your wallet requires a passphrase, you may enter it below.'}
173+ {"If your wallet requires a passphrase, you may enter it below."}
169 </InputCaption>
174 </InputCaption>
170
175
171 <Input
176 <Input
@@ -176,14 +181,15 @@ class Shards extends React.Component {
176 type="password"
181 type="password"
177 value={passphrase}
182 value={passphrase}
178 autocomplete="off"
183 autocomplete="off"
179− onChange={this.handlePassphraseInput}>
184+ onChange={this.handlePassphraseInput}
185+ >
180− <InnerLabel>{'Passphrase'}</InnerLabel>
186+ <InnerLabel>{"Passphrase"}</InnerLabel>
181 </Input>
187 </Input>
182
188
183 <Button
189 <Button
184 disabled={!ready}
190 disabled={!ready}
185− className={'mt-8'}
191+ className={"mt-8"}
186− prop-size={'lg wide'}
192+ prop-size={"lg wide"}
187 // prop-color={this.buttonTriState(wallet)}
193 // prop-color={this.buttonTriState(wallet)}
188 onClick={() =>
194 onClick={() =>
189 this.walletFromShards(
195 this.walletFromShards(
@@ -191,21 +197,20 @@ class Shards extends React.Component {
191 shard2,
197 shard2,
192 shard3,
198 shard3,
193 pointName,
199 pointName,
194− passphrase
200+ passphrase,
195 )
201 )
196− }>
202+ }
203+ >
197− {'Unlock Wallet →'}
204+ {"Unlock Wallet →"}
198 </Button>
205 </Button>
199
206
200 <Button
207 <Button
201− className={'mt-4'}
208+ className={"mt-4"}
202− prop-size={'xl wide'}
209+ prop-size={"xl wide"}
203 disabled={Nothing.hasInstance(wallet)}
210 disabled={Nothing.hasInstance(wallet)}
204− onClick={() => {
205− popRoute();
206− pushRoute(ROUTE_NAMES.SHIPS);
211+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
207− }}>
212+ >
208− {'Continue →'}
213+ {"Continue →"}
209 </Button>
214 </Button>
210 </Col>
215 </Col>
211 </Row>
216 </Row>
@@ -213,4 +218,4 @@ class Shards extends React.Component {
213 }
218 }
214}
219}
215
220
216−export default Shards;
221+export default withHistory(Shards);
src/bridge/views/Ticket.js+36−35
@@ -1,6 +1,6 @@
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,
@@ -8,21 +8,22 @@ import {
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/routeNames";
16+import { withHistory } from "../lib/history";
16−import { urbitWalletFromTicket } from '../lib/wallet';
17+import { urbitWalletFromTicket } from "../lib/wallet";
17
18
18class Ticket extends React.Component {
19class Ticket extends React.Component {
19 constructor(props) {
20 constructor(props) {
20 super(props);
21 super(props);
21
22
22 this.state = {
23 this.state = {
23− ticket: '',
24+ ticket: "",
24− pointName: '',
25+ pointName: "",
25− passphrase: '',
26+ passphrase: "",
26 isUnlocking: false,
27 isUnlocking: false,
27 };
28 };
28
29
@@ -62,7 +63,7 @@ class Ticket extends React.Component {
62 }
63 }
63
64
64 render() {
65 render() {
65− const { popRoute, pushRoute, wallet } = this.props;
66+ const { history, wallet } = this.props;
66 const { ticket, pointName, passphrase } = this.state;
67 const { ticket, pointName, passphrase } = this.state;
67
68
68 const phPoint = this.pointPlaceholder;
69 const phPoint = this.pointPlaceholder;
@@ -71,7 +72,7 @@ class Ticket extends React.Component {
71 return (
72 return (
72 <Row>
73 <Row>
73 <Col>
74 <Col>
74− <H1>{'Authenticate'}</H1>
75+ <H1>{"Authenticate"}</H1>
75
76
76 <P>
77 <P>
77 {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
78 {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
@@ -85,10 +86,11 @@ class Ticket extends React.Component {
85 autoFocus
86 autoFocus
86 placeholder={`e.g. ${phPoint}`}
87 placeholder={`e.g. ${phPoint}`}
87 value={pointName}
88 value={pointName}
88− onChange={this.handlePointNameInput}>
89+ onChange={this.handlePointNameInput}
90+ >
89− <InnerLabel>{'Point'}</InnerLabel>
91+ <InnerLabel>{"Point"}</InnerLabel>
90 <ValidatedSigil
92 <ValidatedSigil
91− className={'tr-0 mt-05 mr-0 abs'}
93+ className={"tr-0 mt-05 mr-0 abs"}
92 patp={pointName}
94 patp={pointName}
93 size={68}
95 size={68}
94 margin={8}
96 margin={8}
@@ -103,12 +105,13 @@ class Ticket extends React.Component {
103 name="ticket"
105 name="ticket"
104 placeholder={`e.g. ${phTick}`}
106 placeholder={`e.g. ${phTick}`}
105 value={ticket}
107 value={ticket}
106− onChange={this.handleTicketInput}>
108+ onChange={this.handleTicketInput}
109+ >
107− <InnerLabel>{'Ticket'}</InnerLabel>
110+ <InnerLabel>{"Ticket"}</InnerLabel>
108 </TicketInput>
111 </TicketInput>
109
112
110 <InputCaption>
113 <InputCaption>
111− {'If your wallet requires a passphrase, you may enter it below.'}
114+ {"If your wallet requires a passphrase, you may enter it below."}
112 </InputCaption>
115 </InputCaption>
113
116
114 <Input
117 <Input
@@ -119,32 +122,30 @@ class Ticket extends React.Component {
119 type="password"
122 type="password"
120 value={passphrase}
123 value={passphrase}
121 autocomplete="off"
124 autocomplete="off"
122− onChange={this.handlePassphraseInput}>
125+ onChange={this.handlePassphraseInput}
126+ >
123− <InnerLabel>{'Passphrase'}</InnerLabel>
127+ <InnerLabel>{"Passphrase"}</InnerLabel>
124 </Input>
128 </Input>
125
129
126 <Button
130 <Button
127− className={'mt-8'}
131+ className={"mt-8"}
128− prop-size={'lg wide'}
132+ prop-size={"lg wide"}
129 disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
133 disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
130− onClick={() =>
131− this.walletFromTicket(ticket, pointName, passphrase)
134+ onClick={() => this.walletFromTicket(ticket, pointName, passphrase)}
132− }>
135+ >
133 <span className="relative">
136 <span className="relative">
134 {this.state.isUnlocking && <span className="btn-spinner" />}
137 {this.state.isUnlocking && <span className="btn-spinner" />}
135− {'Unlock Wallet →'}
138+ {"Unlock Wallet →"}
136 </span>
139 </span>
137 </Button>
140 </Button>
138
141
139 <Button
142 <Button
140− className={'mt-4'}
143+ className={"mt-4"}
141− prop-size={'xl wide'}
144+ prop-size={"xl wide"}
142 disabled={Nothing.hasInstance(wallet)}
145 disabled={Nothing.hasInstance(wallet)}
143− onClick={() => {
144− popRoute();
145− pushRoute(ROUTE_NAMES.SHIPS);
146+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
146− }}>
147+ >
147− {'Continue →'}
148+ {"Continue →"}
148 </Button>
149 </Button>
149 </Col>
150 </Col>
150 </Row>
151 </Row>
@@ -152,4 +153,4 @@ class Ticket extends React.Component {
152 }
153 }
153}
154}
154
155
155−export default Ticket;
156+export default withHistory(Ticket);
src/bridge/views/Transfer.js+27−25
@@ -1,8 +1,8 @@
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
7import {
7import {
8 Row,
8 Row,
@@ -12,13 +12,13 @@ import {
12 ShowBlockie,
12 ShowBlockie,
13 P,
13 P,
14 Anchor,
14 Anchor,
15−} from '../components/Base';
15+} from "../components/Base";
16−import { AddressInput } from '../components/Base';
16+import { AddressInput } from "../components/Base";
17−import StatelessTransaction from '../components/StatelessTransaction';
17+import StatelessTransaction from "../components/StatelessTransaction";
18
18
19−import { NETWORK_NAMES } from '../lib/network';
19+import { NETWORK_NAMES } from "../lib/network";
20
20
21−import { isValidAddress } from '../lib/wallet';
21+import { isValidAddress } from "../lib/wallet";
22
22
23class Transfer extends React.Component {
23class Transfer extends React.Component {
24 constructor(props) {
24 constructor(props) {
@@ -27,7 +27,7 @@ class Transfer extends React.Component {
27 const issuingPoint = need.pointCursor(props);
27 const issuingPoint = need.pointCursor(props);
28
28
29 this.state = {
29 this.state = {
30− receivingAddress: '',
30+ receivingAddress: "",
31 issuingPoint: issuingPoint,
31 issuingPoint: issuingPoint,
32 };
32 };
33
33
@@ -57,7 +57,7 @@ class Transfer extends React.Component {
57 const txn = azimuth.ecliptic.setTransferProxy(
57 const txn = azimuth.ecliptic.setTransferProxy(
58 validContracts,
58 validContracts,
59 validPoint,
59 validPoint,
60− state.receivingAddress
60+ state.receivingAddress,
61 );
61 );
62
62
63 return Maybe.Just(txn);
63 return Maybe.Just(txn);
@@ -75,15 +75,15 @@ class Transfer extends React.Component {
75
75
76 const esdomain =
76 const esdomain =
77 props.networkType === NETWORK_NAMES.ROPSTEN
77 props.networkType === NETWORK_NAMES.ROPSTEN
78− ? 'ropsten.etherscan.io'
78+ ? "ropsten.etherscan.io"
79− : 'etherscan.io';
79+ : "etherscan.io";
80
80
81 return (
81 return (
82 <Row>
82 <Row>
83 <Col>
83 <Col>
84 <H1>
84 <H1>
85− {'Transfer'} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
85+ {"Transfer"} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
86− {'To a New Owner'}
86+ {"To a New Owner"}
87 </H1>
87 </H1>
88
88
89 <P>
89 <P>
@@ -93,25 +93,27 @@ class Transfer extends React.Component {
93 </P>
93 </P>
94
94
95 <AddressInput
95 <AddressInput
96− className={'mono mt-8'}
96+ className={"mono mt-8"}
97 prop-size="lg"
97 prop-size="lg"
98 prop-format="innerLabel"
98 prop-format="innerLabel"
99 value={state.receivingAddress}
99 value={state.receivingAddress}
100 placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
100 placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
101− onChange={v => this.handleAddressInput(v)}>
101+ onChange={v => this.handleAddressInput(v)}
102+ >
102− <InnerLabel>{'New ownership address'}</InnerLabel>
103+ <InnerLabel>{"New ownership address"}</InnerLabel>
103− <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
104+ <ShowBlockie className={"mt-1"} address={state.receivingAddress} />
104 </AddressInput>
105 </AddressInput>
105
106
106 <Anchor
107 <Anchor
107− className={'mt-1'}
108+ className={"mt-1"}
108− prop-size={'s'}
109+ prop-size={"s"}
109 prop-disabled={
110 prop-disabled={
110 !isValidAddress(state.receivingAddress) || !esvisible
111 !isValidAddress(state.receivingAddress) || !esvisible
111 }
112 }
112− target={'_blank'}
113+ target={"_blank"}
113− href={`https://${esdomain}/address/${state.receivingAddress}`}>
114+ href={`https://${esdomain}/address/${state.receivingAddress}`}
115+ >
114− {'View on Etherscan ↗'}
116+ {"View on Etherscan ↗"}
115 </Anchor>
117 </Anchor>
116
118
117 <StatelessTransaction
119 <StatelessTransaction
src/bridge/views/Trezor.js+39−38
@@ -1,14 +1,15 @@
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/routeNames";
12+import { withHistory } from "../lib/history";
12
13
13class Trezor extends React.Component {
14class Trezor extends React.Component {
14 constructor(props) {
15 constructor(props) {
@@ -26,7 +27,7 @@ class Trezor extends React.Component {
26
27
27 handleAccountSelection(account) {
28 handleAccountSelection(account) {
28 let hdpath = this.state.hdpath;
29 let hdpath = this.state.hdpath;
29− if (account !== 'custom') {
30+ if (account !== "custom") {
30 hdpath = TREZOR_PATH.replace(/x/g, account);
31 hdpath = TREZOR_PATH.replace(/x/g, account);
31 }
32 }
32 this.setState({ account, hdpath });
33 this.setState({ account, hdpath });
@@ -41,14 +42,14 @@ class Trezor extends React.Component {
41 const { hdpath } = this.state;
42 const { hdpath } = this.state;
42
43
43 TrezorConnect.manifest({
44 TrezorConnect.manifest({
44− email: 'bridge-trezor@urbit.org',
45+ email: "bridge-trezor@urbit.org",
45− appUrl: 'https://github.com/urbit/bridge',
46+ appUrl: "https://github.com/urbit/bridge",
46 });
47 });
47 TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
48 TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
48 if (info.success === true) {
49 if (info.success === true) {
49 const payload = info.payload;
50 const payload = info.payload;
50− const publicKey = Buffer.from(payload.publicKey, 'hex');
51+ const publicKey = Buffer.from(payload.publicKey, "hex");
51− const chainCode = Buffer.from(payload.chainCode, 'hex');
52+ const chainCode = Buffer.from(payload.chainCode, "hex");
52 const pub = secp256k1.publicKeyConvert(publicKey, true);
53 const pub = secp256k1.publicKeyConvert(publicKey, true);
53 const hd = bip32.fromPublicKey(pub, chainCode);
54 const hd = bip32.fromPublicKey(pub, chainCode);
54 setWallet(Maybe.Just(hd));
55 setWallet(Maybe.Just(hd));
@@ -60,19 +61,19 @@ class Trezor extends React.Component {
60 }
61 }
61
62
62 render() {
63 render() {
63− const { pushRoute, popRoute, wallet } = this.props;
64+ const { history, wallet } = this.props;
64 const { hdpath, account } = this.state;
65 const { hdpath, account } = this.state;
65 const { handleAccountSelection } = this;
66 const { handleAccountSelection } = this;
66
67
67 let accountOptions = [
68 let accountOptions = [
68 {
69 {
69− title: 'Custom path',
70+ title: "Custom path",
70− value: 'custom',
71+ value: "custom",
71 },
72 },
72 ];
73 ];
73 for (let i = 0; i < 20; i++) {
74 for (let i = 0; i < 20; i++) {
74 accountOptions.push({
75 accountOptions.push({
75− title: 'Account #' + (i + 1),
76+ title: "Account #" + (i + 1),
76 value: i,
77 value: i,
77 });
78 });
78 }
79 }
@@ -85,14 +86,14 @@ class Trezor extends React.Component {
85 prop-format="innerLabel"
86 prop-format="innerLabel"
86 options={accountOptions}
87 options={accountOptions}
87 handleUpdate={handleAccountSelection}
88 handleUpdate={handleAccountSelection}
88− title={'Account'}
89+ title={"Account"}
89 currentSelectionTitle={accountTitle}
90 currentSelectionTitle={accountTitle}
90 fullWidth={true}
91 fullWidth={true}
91 />
92 />
92 );
93 );
93
94
94 const pathSelection =
95 const pathSelection =
95− account !== 'custom' ? null : (
96+ account !== "custom" ? null : (
96 <Input
97 <Input
97 className="mt-8 pt-8 text-mono"
98 className="mt-8 pt-8 text-mono"
98 prop-size="md"
99 prop-size="md"
@@ -100,15 +101,16 @@ class Trezor extends React.Component {
100 name="hdpath"
101 name="hdpath"
101 value={hdpath}
102 value={hdpath}
102 autocomplete="off"
103 autocomplete="off"
103− onChange={this.handleHdPathInput}>
104+ onChange={this.handleHdPathInput}
105+ >
104− <InnerLabel>{'HD Path'}</InnerLabel>
106+ <InnerLabel>{"HD Path"}</InnerLabel>
105 </Input>
107 </Input>
106 );
108 );
107
109
108 return (
110 return (
109 <Row>
111 <Row>
110− <Col className={'measure-md'}>
112+ <Col className={"measure-md"}>
111− <H1>{'Authenticate With Your Trezor'}</H1>
113+ <H1>{"Authenticate With Your Trezor"}</H1>
112
114
113 <P>
115 <P>
114 {`Connect and authenticate to your Trezor. If you'd like
116 {`Connect and authenticate to your Trezor. If you'd like
@@ -119,21 +121,20 @@ class Trezor extends React.Component {
119 {pathSelection}
121 {pathSelection}
120
122
121 <Button
123 <Button
122− className={'mt-8'}
124+ className={"mt-8"}
123− prop-size={'wide lg'}
125+ prop-size={"wide lg"}
124− onClick={this.pollDevice}>
126+ onClick={this.pollDevice}
127+ >
125− {'Authenticate →'}
128+ {"Authenticate →"}
126 </Button>
129 </Button>
127
130
128 <Button
131 <Button
129− className={'mt-8'}
132+ className={"mt-8"}
130− prop-size={'wide lg'}
133+ prop-size={"wide lg"}
131 disabled={Maybe.Nothing.hasInstance(wallet)}
134 disabled={Maybe.Nothing.hasInstance(wallet)}
132− onClick={() => {
133− popRoute();
134− pushRoute(ROUTE_NAMES.SHIPS);
135+ onClick={() => history.popAndPush(ROUTE_NAMES.SHIPS)}
135− }}>
136+ >
136− {'Continue →'}
137+ {"Continue →"}
137 </Button>
138 </Button>
138 </Col>
139 </Col>
139 </Row>
140 </Row>
@@ -141,4 +142,4 @@ class Trezor extends React.Component {
141 }
142 }
142}
143}
143
144
144−export default Trezor;
145+export default withHistory(Trezor);
src/bridge/views/ViewPoint.js+22−20
@@ -1,19 +1,20 @@
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/routeNames";
10+import { withHistory } from "../lib/history";
10
11
11class ViewPoint extends React.Component {
12class ViewPoint extends React.Component {
12 constructor(props) {
13 constructor(props) {
13 super(props);
14 super(props);
14
15
15 this.state = {
16 this.state = {
16− pointName: '',
17+ pointName: "",
17 };
18 };
18
19
19 this.handlePointInput = this.handlePointInput.bind(this);
20 this.handlePointInput = this.handlePointInput.bind(this);
@@ -27,7 +28,7 @@ class ViewPoint extends React.Component {
27
28
28 render() {
29 render() {
29 const { pointName } = this.state;
30 const { pointName } = this.state;
30− const { popRoute, pushRoute, setPointCursor } = this.props;
31+ const { history, setPointCursor } = this.props;
31
32
32 // NB (jtobin):
33 // NB (jtobin):
33 //
34 //
@@ -42,9 +43,9 @@ class ViewPoint extends React.Component {
42 return (
43 return (
43 <Row>
44 <Row>
44 <Col>
45 <Col>
45− <H1>{'View a Point'}</H1>
46+ <H1>{"View a Point"}</H1>
46
47
47− <P>{'Enter a point name to view its public information.'}</P>
48+ <P>{"Enter a point name to view its public information."}</P>
48
49
49 <PointInput
50 <PointInput
50 autoFocus
51 autoFocus
@@ -53,10 +54,11 @@ class ViewPoint extends React.Component {
53 className="mono"
54 className="mono"
54 placeholder="e.g. ~zod"
55 placeholder="e.g. ~zod"
55 onChange={this.handlePointInput}
56 onChange={this.handlePointInput}
56− value={pointName}>
57+ value={pointName}
58+ >
57− <InnerLabel>{'Point Name'}</InnerLabel>
59+ <InnerLabel>{"Point Name"}</InnerLabel>
58 <ValidatedSigil
60 <ValidatedSigil
59− className={'tr-0 mt-05 mr-0 abs'}
61+ className={"tr-0 mt-05 mr-0 abs"}
60 patp={pointName}
62 patp={pointName}
61 show
63 show
62 size={68}
64 size={68}
@@ -65,14 +67,14 @@ class ViewPoint extends React.Component {
65 </PointInput>
67 </PointInput>
66
68
67 <Button
69 <Button
68− className={'mt-8'}
70+ className={"mt-8"}
69 disabled={valid === false}
71 disabled={valid === false}
70 onClick={() => {
72 onClick={() => {
71 setPointCursor(Maybe.Just(ob.patp2dec(pointName)));
73 setPointCursor(Maybe.Just(ob.patp2dec(pointName)));
72− popRoute();
73− pushRoute(ROUTE_NAMES.SHIP);
74+ history.popAndPush(ROUTE_NAMES.SHIP);
74− }}>
75+ }}
76+ >
75− {'Continue →'}
77+ {"Continue →"}
76 </Button>
78 </Button>
77 </Col>
79 </Col>
78 </Row>
80 </Row>
@@ -80,4 +82,4 @@ class ViewPoint extends React.Component {
80 }
82 }
81}
83}
82
84
83−export default ViewPoint;
85+export default withHistory(ViewPoint);
src/bridge/views/Wallet.js+27−25
@@ -1,11 +1,12 @@
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/routeNames";
8+import { withHistory } from "../lib/history";
8−import { WALLET_NAMES, renderWalletType } from '../lib/wallet';
9+import { WALLET_NAMES, renderWalletType } from "../lib/wallet";
9
10
10class Wallet extends React.Component {
11class Wallet extends React.Component {
11 componentDidMount() {
12 componentDidMount() {
@@ -16,37 +17,37 @@ class Wallet extends React.Component {
16 getWalletOptions() {
17 getWalletOptions() {
17 return [
18 return [
18 {
19 {
19− title: 'Urbit Master Ticket',
20+ title: "Urbit Master Ticket",
20 value: WALLET_NAMES.TICKET,
21 value: WALLET_NAMES.TICKET,
21 },
22 },
22 {
23 {
23− title: 'Urbit Master Ticket (Shards)',
24+ title: "Urbit Master Ticket (Shards)",
24 value: WALLET_NAMES.SHARDS,
25 value: WALLET_NAMES.SHARDS,
25 },
26 },
26 {
27 {
27− type: 'divider',
28+ type: "divider",
28 },
29 },
29 {
30 {
30− title: 'Ledger',
31+ title: "Ledger",
31 value: WALLET_NAMES.LEDGER,
32 value: WALLET_NAMES.LEDGER,
32 },
33 },
33 {
34 {
34− title: 'Trezor',
35+ title: "Trezor",
35 value: WALLET_NAMES.TREZOR,
36 value: WALLET_NAMES.TREZOR,
36 },
37 },
37 {
38 {
38− type: 'divider',
39+ type: "divider",
39 },
40 },
40 {
41 {
41− title: 'BIP39 Mnemonic',
42+ title: "BIP39 Mnemonic",
42 value: WALLET_NAMES.MNEMONIC,
43 value: WALLET_NAMES.MNEMONIC,
43 },
44 },
44 {
45 {
45− title: 'Ethereum Private Key',
46+ title: "Ethereum Private Key",
46 value: WALLET_NAMES.PRIVATE_KEY,
47 value: WALLET_NAMES.PRIVATE_KEY,
47 },
48 },
48 {
49 {
49− title: 'Ethereum Keystore File',
50+ title: "Ethereum Keystore File",
50 value: WALLET_NAMES.KEYSTORE,
51 value: WALLET_NAMES.KEYSTORE,
51 },
52 },
52 ];
53 ];
@@ -59,7 +60,7 @@ class Wallet extends React.Component {
59 return (
60 return (
60 <Row>
61 <Row>
61 <Col>
62 <Col>
62− <H1>{'Unlock a Wallet'}</H1>
63+ <H1>{"Unlock a Wallet"}</H1>
63
64
64 <P>
65 <P>
65 {`To manage your assets, you need to unlock a wallet.
66 {`To manage your assets, you need to unlock a wallet.
@@ -67,17 +68,17 @@ class Wallet extends React.Component {
67 </P>
68 </P>
68
69
69 <InnerLabelDropdown
70 <InnerLabelDropdown
70− className={'mb-10 mt-6'}
71+ className={"mb-10 mt-6"}
71− title={'Wallet Type:'}
72+ title={"Wallet Type:"}
72 options={walletOptions}
73 options={walletOptions}
73 handleUpdate={props.setWalletType}
74 handleUpdate={props.setWalletType}
74 currentSelectionTitle={renderWalletType(props.walletType)}
75 currentSelectionTitle={renderWalletType(props.walletType)}
75 />
76 />
76
77
77 <Button
78 <Button
78− className={'mt-10'}
79+ className={"mt-10"}
79 onClick={() =>
80 onClick={() =>
80− props.pushRoute(
81+ props.history.push(
81 props.walletType === WALLET_NAMES.MNEMONIC
82 props.walletType === WALLET_NAMES.MNEMONIC
82 ? ROUTE_NAMES.MNEMONIC
83 ? ROUTE_NAMES.MNEMONIC
83 : props.walletType === WALLET_NAMES.TICKET
84 : props.walletType === WALLET_NAMES.TICKET
@@ -92,10 +93,11 @@ class Wallet extends React.Component {
92 ? ROUTE_NAMES.PRIVATE_KEY
93 ? ROUTE_NAMES.PRIVATE_KEY
93 : props.walletType === WALLET_NAMES.KEYSTORE
94 : props.walletType === WALLET_NAMES.KEYSTORE
94 ? ROUTE_NAMES.KEYSTORE
95 ? ROUTE_NAMES.KEYSTORE
95− : ROUTE_NAMES.DEFAULT
96+ : ROUTE_NAMES.DEFAULT,
96 )
97 )
97− }>
98+ }
99+ >
98− {'Continue →'}
100+ {"Continue →"}
99 </Button>
101 </Button>
100 </Col>
102 </Col>
101 </Row>
103 </Row>
@@ -103,4 +105,4 @@ class Wallet extends React.Component {
103 }
105 }
104}
106}
105
107
106−export default Wallet;
108+export default withHistory(Wallet);
src/walletgen/lib/lib.js+9−9
@@ -1,7 +1,7 @@
1−import * as ob from 'urbit-ob';
1+import * as ob from "urbit-ob";
2−import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
2+import * as kg from "urbit-key-generation/dist/index";
3−import * as more from 'more-entropy';
3+import * as more from "more-entropy";
4−import lodash from 'lodash';
4+import lodash from "lodash";
5import {
5import {
6 MIN_STAR,
6 MIN_STAR,
7 MIN_PLANET,
7 MIN_PLANET,
@@ -9,7 +9,7 @@ import {
9 GALAXY_ENTROPY_BITS,
9 GALAXY_ENTROPY_BITS,
10 STAR_ENTROPY_BITS,
10 STAR_ENTROPY_BITS,
11 PLANET_ENTROPY_BITS,
11 PLANET_ENTROPY_BITS,
12−} from './constants';
12+} from "./constants";
13
13
14const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8;
14const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8;
15
15
@@ -35,9 +35,9 @@ const makeTicket = point => {
35 const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1]);
35 const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1]);
36 const entropy = lodash.zipWith(some, more, (x, y) => x ^ y);
36 const entropy = lodash.zipWith(some, more, (x, y) => x ^ y);
37 const buf = Buffer.from(entropy);
37 const buf = Buffer.from(entropy);
38− const patq = ob.hex2patq(buf.toString('hex'));
38+ const patq = ob.hex2patq(buf.toString("hex"));
39 resolve(patq);
39 resolve(patq);
40− reject('Entropy generation failed');
40+ reject("Entropy generation failed");
41 });
41 });
42 });
42 });
43};
43};
@@ -48,7 +48,7 @@ const generateWallet = async (point, ticket, boot) => {
48 ticket: ticket,
48 ticket: ticket,
49 seedSize: SEED_LENGTH_BYTES,
49 seedSize: SEED_LENGTH_BYTES,
50 ship: point,
50 ship: point,
51− password: '',
51+ password: "",
52 revisions: {},
52 revisions: {},
53 boot: boot,
53 boot: boot,
54 };
54 };
@@ -57,7 +57,7 @@ const generateWallet = async (point, ticket, boot) => {
57
57
58 // This is here to notify anyone who opens console because the thread
58 // This is here to notify anyone who opens console because the thread
59 // hangs, blocking UI updates so this cannot be done in the UI
59 // hangs, blocking UI updates so this cannot be done in the UI
60− console.log('Generating Wallet for point address: ', point);
60+ console.log("Generating Wallet for point address: ", point);
61
61
62 return wallet;
62 return wallet;
63};
63};