on master help sign in
Merge pull request #146 from urbit/feat/prettier

Add prettier as a project linter and format src/

commit1281e198d2cf56945d30e8fd48723793e1fc96ef authorFang <Fang-@users.noreply.github.com> dateTue 11 Jun 2019, 18:41 (7 years ago) parent899620e82e8d5b
131 files changed+6627−6121
.eslintrc+9 .prettierignore+1 .prettierrc.json+7 package-lock.json+293−3 package.json+8−1 src/Root.js+31−37 src/bridge/Bridge.js+174−176 src/bridge/components/Anchor.js+13−18 src/bridge/components/Base.js+40−87 src/bridge/components/Blockie.js+13−8 src/bridge/components/Breadcrumb.js+4−7 src/bridge/components/BreadcrumbItem.js+4−7 src/bridge/components/Button.js+3−12 src/bridge/components/Checkbox.js+22−19 src/bridge/components/CheckboxButton.js+28−35 src/bridge/components/Code.js+4−7 src/bridge/components/Col.js+3−9 src/bridge/components/Container.js+3−9 src/bridge/components/DropdownDivider.js+1−2 src/bridge/components/DropdownItem.js+2−5 src/bridge/components/FlexCol.js+3−9 src/bridge/components/FlexRow.js+5−14 src/bridge/components/Footer.js+7−15 src/bridge/components/Form.js+3−9 src/bridge/components/H1.js+7−8 src/bridge/components/H2.js+4−7 src/bridge/components/H3.js+4−7 src/bridge/components/H4.js+4−7 src/bridge/components/Header.js+44−40 src/bridge/components/HorizontalSelector.js+11−17 src/bridge/components/InnerLabel.js+5−6 src/bridge/components/InnerLabelDropdown.js+36−35 src/bridge/components/Input.js+30−36 src/bridge/components/InputCaption.js+5−6 src/bridge/components/Label.js+2−5 src/bridge/components/OldHeader.js+38−41 src/bridge/components/OldLink.js+9−16 src/bridge/components/OldTextArea.js+8−15 src/bridge/components/P.js+6−9 src/bridge/components/Passport.js+125−129 src/bridge/components/PointList.js+24−26 src/bridge/components/ProtoAnchor.js+15−20 src/bridge/components/ProtoButton.js+16−17 src/bridge/components/ProtoInput.js+30−32 src/bridge/components/ProtoTextarea.js+28−30 src/bridge/components/ReactSVGComponents.js+42−22 src/bridge/components/RenderedPoint.js+36−35 src/bridge/components/Row.js+4−9 src/bridge/components/RowCol.js+5−10 src/bridge/components/ShowBlockie.js+32−43 src/bridge/components/Spacing.js+1−8 src/bridge/components/StatelessTransaction.js+297−281 src/bridge/components/Textarea.js+30−37 src/bridge/components/UploadButton.js+12−10 src/bridge/components/ValidatedBlockie.js+24−26 src/bridge/components/ValidatedSigil.js+33−37 src/bridge/components/Warning.js+4−9 src/bridge/components/advancedInput.js+53−59 src/bridge/components/icons/Chevron.js+6−7 src/bridge/lib/base.js+5−8 src/bridge/lib/child.js+11−13 src/bridge/lib/contracts.js+5−9 src/bridge/lib/error.js+19−27 src/bridge/lib/invite.js+105−78 src/bridge/lib/inviteMail.js+13−17 src/bridge/lib/keys.js+58−65 src/bridge/lib/ledger.js+16−23 src/bridge/lib/lib.js+19−18 src/bridge/lib/need.js+42−29 src/bridge/lib/network.js+12−15 src/bridge/lib/proxy.js+10−15 src/bridge/lib/router.js+84−115 src/bridge/lib/state.js+5−17 src/bridge/lib/tank.js+37−31 src/bridge/lib/transformers.js+2−3 src/bridge/lib/trezor.js+14−20 src/bridge/lib/txn.js+121−123 src/bridge/lib/validators.js+100−91 src/bridge/lib/wallet.js+82−84 src/bridge/views/AcceptTransfer.js+59−50 src/bridge/views/CancelTransfer.js+35−37 src/bridge/views/CreateGalaxy.js+96−106 src/bridge/views/GenKeyfile.js+56−67 src/bridge/views/InviteTicket.js+224−171 src/bridge/views/InvitesManage.js+76−69 src/bridge/views/InvitesSend.js+154−129 src/bridge/views/IssueChild.js+130−128 src/bridge/views/Keystore.js+104−108 src/bridge/views/Landing.js+50−60 src/bridge/views/Ledger.js+144−135 src/bridge/views/Mnemonic.js+105−116 src/bridge/views/Network.js+90−90 src/bridge/views/Point.js+85−87 src/bridge/views/Point/Actions.js+132−163 src/bridge/views/Point/KeysAndMetadata.js+124−146 src/bridge/views/Points.js+146−138 src/bridge/views/PrivateKey.js+51−56 src/bridge/views/SentTransaction.js+104−113 src/bridge/views/SetKeys.js+81−70 src/bridge/views/SetProxy.js+97−102 src/bridge/views/Shards.js+111−117 src/bridge/views/Ticket.js+72−79 src/bridge/views/Transfer.js+56−49 src/bridge/views/Trezor.js+89−95 src/bridge/views/ViewPoint.js+38−43 src/bridge/views/Wallet.js+76−70 src/common/components/AppNavigation.js+16−20 src/index.js+5−8 src/walletgen/Walletgen.js+64−86 src/walletgen/components/Button.js+5−9 src/walletgen/components/CheckBox.js+22−12 src/walletgen/components/EulaText.js+434−68 src/walletgen/components/Footer.js+24−28 src/walletgen/components/Header.js+62−38 src/walletgen/components/InputBox.js+9−9 src/walletgen/components/ReactSVGComponents.js+37−21 src/walletgen/components/UploadButton.js+14−12 src/walletgen/lib/constants.js+16−16 src/walletgen/lib/lib.js+38−41 src/walletgen/views/BrowserWarning.js+6−7 src/walletgen/views/Custody.js+9−8 src/walletgen/views/Done.js+28−12 src/walletgen/views/Download.js+105−84 src/walletgen/views/Eula.js+21−22 src/walletgen/views/EulaView.js+14−16 src/walletgen/views/Generate.js+221−178 src/walletgen/views/NetworkWarning.js+6−6 src/walletgen/views/PrivacyPolicy.js+229−305 src/walletgen/views/Understand.js+37−14 src/walletgen/views/Upload.js+126−105 src/walletgen/views/Welcome.js+23−10
.eslintrcnew file+9
@@ -0,0 +1,9 @@
1+{
2+ "extends": "react-app",
3+ "plugins": [
4+ "prettier"
5+ ],
6+ "rules": {
7+ "prettier/prettier": "error"
8+ }
9+}
.prettierignorenew file+1
@@ -0,0 +1 @@
1+nockjs/
.prettierrc.jsonnew file+7
@@ -0,0 +1,7 @@
1+{
2+ "semi": true,
3+ "singleQuote": true,
4+ "trailingComma": "es5",
5+ "jsxBracketSameLine": true,
6+ "printWidth": 80
7+}
package-lock.json+293−3
@@ -1049,6 +1049,12 @@
10491049 "resolved": "https://registry.npmjs.org/@types/node/-/node-10.14.4.tgz",
10501050 "integrity": "sha512-DT25xX/YgyPKiHFOpNuANIQIVvYEwCWXgK2jYYwqgaMrYE6+tq+DtmMwlD3drl6DJbUwtlIDnn0d7tIn/EbXBg=="
10511051 },
1052+ "@types/normalize-package-data": {
1053+ "version": "2.4.0",
1054+ "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
1055+ "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
1056+ "dev": true
1057+ },
10521058 "@types/q": {
10531059 "version": "1.5.1",
10541060 "resolved": "https://registry.npmjs.org/@types/q/-/q-1.5.1.tgz",
@@ -5052,6 +5058,15 @@
50525058 "jsx-ast-utils": "^2.0.1"
50535059 }
50545060 },
5061+ "eslint-plugin-prettier": {
5062+ "version": "3.1.0",
5063+ "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.0.tgz",
5064+ "integrity": "sha512-XWX2yVuwVNLOUhQijAkXz+rMPPoCr7WFiAl8ig6I7Xn+pPVhDhzg4DxHpmbeb0iqjO9UronEA3Tb09ChnFVHHA==",
5065+ "dev": true,
5066+ "requires": {
5067+ "prettier-linter-helpers": "^1.0.0"
5068+ }
5069+ },
50555070 "eslint-plugin-react": {
50565071 "version": "7.11.1",
50575072 "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.11.1.tgz",
@@ -5493,6 +5508,12 @@
54935508 "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz",
54945509 "integrity": "sha1-ewUhjd+WZ79/Nwv3/bLLFf3Qqkk="
54955510 },
5511+ "fast-diff": {
5512+ "version": "1.2.0",
5513+ "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
5514+ "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
5515+ "dev": true
5516+ },
54965517 "fast-glob": {
54975518 "version": "2.2.4",
54985519 "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-2.2.4.tgz",
@@ -7299,6 +7320,12 @@
72997320 "resolved": "https://registry.npmjs.org/get-own-enumerable-property-symbols/-/get-own-enumerable-property-symbols-3.0.0.tgz",
73007321 "integrity": "sha512-CIJYJC4GGF06TakLg8z4GQKvDsx9EMspVxOYih7LerEL/WosUnFIww45CGfxfeKHqlg3twgUrYRT1O3WQqjGCg=="
73017322 },
7323+ "get-stdin": {
7324+ "version": "7.0.0",
7325+ "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz",
7326+ "integrity": "sha512-zRKcywvrXlXsA0v0i9Io4KDRaAw7+a1ZpjRwl9Wox8PFlVCCHra7E9c4kqXCoCM9nR5tBkaTTZRBoCm60bFqTQ==",
7327+ "dev": true
7328+ },
73027329 "get-stream": {
73037330 "version": "3.0.0",
73047331 "resolved": "http://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz",
@@ -8187,6 +8214,233 @@
81878214 "resolved": "https://registry.npmjs.org/https-browserify/-/https-browserify-1.0.0.tgz",
81888215 "integrity": "sha1-7AbBDgo0wPL68Zn3/X/Hj//QPHM="
81898216 },
8217+ "husky": {
8218+ "version": "2.4.0",
8219+ "resolved": "https://registry.npmjs.org/husky/-/husky-2.4.0.tgz",
8220+ "integrity": "sha512-3k1wuZU20gFkphNWMjh2ISCFaqfbaLY7R9FST2Mj9HeRhUK9ydj9qQR8qfXlog3EctVGsyeilcZkIT7uBZDDVA==",
8221+ "dev": true,
8222+ "requires": {
8223+ "cosmiconfig": "^5.2.0",
8224+ "execa": "^1.0.0",
8225+ "find-up": "^3.0.0",
8226+ "get-stdin": "^7.0.0",
8227+ "is-ci": "^2.0.0",
8228+ "pkg-dir": "^4.1.0",
8229+ "please-upgrade-node": "^3.1.1",
8230+ "read-pkg": "^5.1.1",
8231+ "run-node": "^1.0.0",
8232+ "slash": "^3.0.0"
8233+ },
8234+ "dependencies": {
8235+ "ci-info": {
8236+ "version": "2.0.0",
8237+ "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-2.0.0.tgz",
8238+ "integrity": "sha512-5tK7EtrZ0N+OLFMthtqOj4fI2Jeb88C4CAZPu25LDVUgXJ0A3Js4PMGqrn0JU1W0Mh1/Z8wZzYPxqUrXeBboCQ==",
8239+ "dev": true
8240+ },
8241+ "cosmiconfig": {
8242+ "version": "5.2.1",
8243+ "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-5.2.1.tgz",
8244+ "integrity": "sha512-H65gsXo1SKjf8zmrJ67eJk8aIRKV5ff2D4uKZIBZShbhGSpEmsQOPW/SKMKYhSTrqR7ufy6RP69rPogdaPh/kA==",
8245+ "dev": true,
8246+ "requires": {
8247+ "import-fresh": "^2.0.0",
8248+ "is-directory": "^0.3.1",
8249+ "js-yaml": "^3.13.1",
8250+ "parse-json": "^4.0.0"
8251+ }
8252+ },
8253+ "execa": {
8254+ "version": "1.0.0",
8255+ "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
8256+ "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
8257+ "dev": true,
8258+ "requires": {
8259+ "cross-spawn": "^6.0.0",
8260+ "get-stream": "^4.0.0",
8261+ "is-stream": "^1.1.0",
8262+ "npm-run-path": "^2.0.0",
8263+ "p-finally": "^1.0.0",
8264+ "signal-exit": "^3.0.0",
8265+ "strip-eof": "^1.0.0"
8266+ }
8267+ },
8268+ "find-up": {
8269+ "version": "3.0.0",
8270+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
8271+ "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
8272+ "dev": true,
8273+ "requires": {
8274+ "locate-path": "^3.0.0"
8275+ }
8276+ },
8277+ "get-stream": {
8278+ "version": "4.1.0",
8279+ "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz",
8280+ "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==",
8281+ "dev": true,
8282+ "requires": {
8283+ "pump": "^3.0.0"
8284+ }
8285+ },
8286+ "is-ci": {
8287+ "version": "2.0.0",
8288+ "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-2.0.0.tgz",
8289+ "integrity": "sha512-YfJT7rkpQB0updsdHLGWrvhBJfcfzNNawYDNIyQXJz0IViGf75O8EBPKSdvw2rF+LGCsX4FZ8tcr3b19LcZq4w==",
8290+ "dev": true,
8291+ "requires": {
8292+ "ci-info": "^2.0.0"
8293+ }
8294+ },
8295+ "js-yaml": {
8296+ "version": "3.13.1",
8297+ "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
8298+ "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
8299+ "dev": true,
8300+ "requires": {
8301+ "argparse": "^1.0.7",
8302+ "esprima": "^4.0.0"
8303+ }
8304+ },
8305+ "locate-path": {
8306+ "version": "3.0.0",
8307+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-3.0.0.tgz",
8308+ "integrity": "sha512-7AO748wWnIhNqAuaty2ZWHkQHRSNfPVIsPIfwEOWO22AmaoVrWavlOcMR5nzTLNYvp36X220/maaRsrec1G65A==",
8309+ "dev": true,
8310+ "requires": {
8311+ "p-locate": "^3.0.0",
8312+ "path-exists": "^3.0.0"
8313+ }
8314+ },
8315+ "normalize-package-data": {
8316+ "version": "2.5.0",
8317+ "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-2.5.0.tgz",
8318+ "integrity": "sha512-/5CMN3T0R4XTj4DcGaexo+roZSdSFW/0AOOTROrjxzCG1wrWXEsGbRKevjlIL+ZDE4sZlJr5ED4YW0yqmkK+eA==",
8319+ "dev": true,
8320+ "requires": {
8321+ "hosted-git-info": "^2.1.4",
8322+ "resolve": "^1.10.0",
8323+ "semver": "2 || 3 || 4 || 5",
8324+ "validate-npm-package-license": "^3.0.1"
8325+ }
8326+ },
8327+ "p-limit": {
8328+ "version": "2.2.0",
8329+ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
8330+ "integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
8331+ "dev": true,
8332+ "requires": {
8333+ "p-try": "^2.0.0"
8334+ }
8335+ },
8336+ "p-locate": {
8337+ "version": "3.0.0",
8338+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-3.0.0.tgz",
8339+ "integrity": "sha512-x+12w/To+4GFfgJhBEpiDcLozRJGegY+Ei7/z0tSLkMmxGZNybVMSfWj9aJn8Z5Fc7dBUNJOOVgPv2H7IwulSQ==",
8340+ "dev": true,
8341+ "requires": {
8342+ "p-limit": "^2.0.0"
8343+ }
8344+ },
8345+ "p-try": {
8346+ "version": "2.2.0",
8347+ "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz",
8348+ "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==",
8349+ "dev": true
8350+ },
8351+ "parse-json": {
8352+ "version": "4.0.0",
8353+ "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-4.0.0.tgz",
8354+ "integrity": "sha1-vjX1Qlvh9/bHRxhPmKeIy5lHfuA=",
8355+ "dev": true,
8356+ "requires": {
8357+ "error-ex": "^1.3.1",
8358+ "json-parse-better-errors": "^1.0.1"
8359+ }
8360+ },
8361+ "path-exists": {
8362+ "version": "3.0.0",
8363+ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-3.0.0.tgz",
8364+ "integrity": "sha1-zg6+ql94yxiSXqfYENe1mwEP1RU=",
8365+ "dev": true
8366+ },
8367+ "pkg-dir": {
8368+ "version": "4.2.0",
8369+ "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz",
8370+ "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==",
8371+ "dev": true,
8372+ "requires": {
8373+ "find-up": "^4.0.0"
8374+ },
8375+ "dependencies": {
8376+ "find-up": {
8377+ "version": "4.0.0",
8378+ "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.0.0.tgz",
8379+ "integrity": "sha512-zoH7ZWPkRdgwYCDVoQTzqjG8JSPANhtvLhh4KVUHyKnaUJJrNeFmWIkTcNuJmR3GLMEmGYEf2S2bjgx26JTF+Q==",
8380+ "dev": true,
8381+ "requires": {
8382+ "locate-path": "^5.0.0"
8383+ }
8384+ },
8385+ "locate-path": {
8386+ "version": "5.0.0",
8387+ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz",
8388+ "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==",
8389+ "dev": true,
8390+ "requires": {
8391+ "p-locate": "^4.1.0"
8392+ }
8393+ },
8394+ "p-locate": {
8395+ "version": "4.1.0",
8396+ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz",
8397+ "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==",
8398+ "dev": true,
8399+ "requires": {
8400+ "p-limit": "^2.2.0"
8401+ }
8402+ }
8403+ }
8404+ },
8405+ "pump": {
8406+ "version": "3.0.0",
8407+ "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz",
8408+ "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==",
8409+ "dev": true,
8410+ "requires": {
8411+ "end-of-stream": "^1.1.0",
8412+ "once": "^1.3.1"
8413+ }
8414+ },
8415+ "read-pkg": {
8416+ "version": "5.1.1",
8417+ "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.1.1.tgz",
8418+ "integrity": "sha512-dFcTLQi6BZ+aFUaICg7er+/usEoqFdQxiEBsEMNGoipenihtxxtdrQuBXvyANCEI8VuUIVYFgeHGx9sLLvim4w==",
8419+ "dev": true,
8420+ "requires": {
8421+ "@types/normalize-package-data": "^2.4.0",
8422+ "normalize-package-data": "^2.5.0",
8423+ "parse-json": "^4.0.0",
8424+ "type-fest": "^0.4.1"
8425+ }
8426+ },
8427+ "resolve": {
8428+ "version": "1.11.1",
8429+ "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.11.1.tgz",
8430+ "integrity": "sha512-vIpgF6wfuJOZI7KKKSP+HmiKggadPQAdsp5HiC1mvqnfp0gF1vdwgBWZIdrVft9pgqoMFQN+R7BSWZiBxx+BBw==",
8431+ "dev": true,
8432+ "requires": {
8433+ "path-parse": "^1.0.6"
8434+ }
8435+ },
8436+ "slash": {
8437+ "version": "3.0.0",
8438+ "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
8439+ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
8440+ "dev": true
8441+ }
8442+ }
8443+ },
81908444 "iced-runtime": {
81918445 "version": "1.0.3",
81928446 "resolved": "https://registry.npmjs.org/iced-runtime/-/iced-runtime-1.0.3.tgz",
@@ -11719,6 +11973,15 @@
1171911973 }
1172011974 }
1172111975 },
11976+ "please-upgrade-node": {
11977+ "version": "3.1.1",
11978+ "resolved": "https://registry.npmjs.org/please-upgrade-node/-/please-upgrade-node-3.1.1.tgz",
11979+ "integrity": "sha512-KY1uHnQ2NlQHqIJQpnh/i54rKkuxCEBx+voJIS/Mvb+L2iYd2NMotwduhKTMjfC1uKoX3VXOxLjIYG66dfJTVQ==",
11980+ "dev": true,
11981+ "requires": {
11982+ "semver-compare": "^1.0.0"
11983+ }
11984+ },
1172211985 "pluralize": {
1172311986 "version": "7.0.0",
1172411987 "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-7.0.0.tgz",
@@ -15424,9 +15687,18 @@
1542415687 "integrity": "sha1-gV7R9uvGWSb4ZbMQwHE7yzMVzks="
1542515688 },
1542615689 "prettier": {
15427− "version": "1.15.3",
15428− "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.15.3.tgz",
15429− "integrity": "sha512-gAU9AGAPMaKb3NNSUUuhhFAS7SCO4ALTN4nRIn6PJ075Qd28Yn2Ig2ahEJWdJwJmlEBTUfC7mMUSFy8MwsOCfg=="
15690+ "version": "1.18.2",
15691+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-1.18.2.tgz",
15692+ "integrity": "sha512-OeHeMc0JhFE9idD4ZdtNibzY0+TPHSpSSb9h8FqtP+YnoZZ1sl8Vc9b1sasjfymH3SonAF4QcA2+mzHPhMvIiw=="
15693+ },
15694+ "prettier-linter-helpers": {
15695+ "version": "1.0.0",
15696+ "resolved": "https://registry.npmjs.org/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz",
15697+ "integrity": "sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w==",
15698+ "dev": true,
15699+ "requires": {
15700+ "fast-diff": "^1.1.2"
15701+ }
1543015702 },
1543115703 "pretty-bytes": {
1543215704 "version": "4.0.2",
@@ -16779,6 +17051,12 @@
1677917051 "is-promise": "^2.1.0"
1678017052 }
1678117053 },
17054+ "run-node": {
17055+ "version": "1.0.0",
17056+ "resolved": "https://registry.npmjs.org/run-node/-/run-node-1.0.0.tgz",
17057+ "integrity": "sha512-kc120TBlQ3mih1LSzdAJXo4xn/GWS2ec0l3S+syHDXP9uRr0JAT8Qd3mdMuyjqCzeZktgP3try92cEgf9Nks8A==",
17058+ "dev": true
17059+ },
1678217060 "run-queue": {
1678317061 "version": "1.0.3",
1678417062 "resolved": "https://registry.npmjs.org/run-queue/-/run-queue-1.0.3.tgz",
@@ -17253,6 +17531,12 @@
1725317531 "resolved": "https://registry.npmjs.org/semver/-/semver-5.6.0.tgz",
1725417532 "integrity": "sha512-RS9R6R35NYgQn++fkDWaOmqGoj4Ek9gGs+DPxNUZKuwE183xjJroKvyo1IzVFeXvUrvmALy6FWD5xrdJT25gMg=="
1725517533 },
17534+ "semver-compare": {
17535+ "version": "1.0.0",
17536+ "resolved": "https://registry.npmjs.org/semver-compare/-/semver-compare-1.0.0.tgz",
17537+ "integrity": "sha1-De4hahyUGrN+nvsXiPavxf9VN/w=",
17538+ "dev": true
17539+ },
1725617540 "send": {
1725717541 "version": "0.16.2",
1725817542 "resolved": "https://registry.npmjs.org/send/-/send-0.16.2.tgz",
@@ -18737,6 +19021,12 @@
1873719021 "prelude-ls": "~1.1.2"
1873819022 }
1873919023 },
19024+ "type-fest": {
19025+ "version": "0.4.1",
19026+ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.4.1.tgz",
19027+ "integrity": "sha512-IwzA/LSfD2vC1/YDYMv/zHP4rDF1usCwllsDpbolT3D4fUepIO7f9K70jjmUewU/LmGUKJcwcVtDCpnKk4BPMw==",
19028+ "dev": true
19029+ },
1874019030 "type-is": {
1874119031 "version": "1.6.16",
1874219032 "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.16.tgz",
package.json+8−1
@@ -60,11 +60,18 @@
6060 "pilot:deploy": "cd node_modules/azimuth-solidity && truffle deploy",
6161 "pilot:setup": "npm run pilot:ganache && npm run pilot:deploy",
6262 "pilot": "HTTPS=true npm-run-all pilot:setup start pilot:cleanup --continue-on-error",
63− "pilot:cleanup": "pkill -f ganache-cli"
63+ "pilot:cleanup": "pkill -f ganache-cli",
64+ "lint": "prettier src/**/*.js",
65+ "lint:check": "prettier -c 'src/**/*.js'",
66+ "lint:fix": "prettier --write 'src/**/*.js'",
67+ "precommit": "npm-run-all lint:check"
6468 },
6569 "devDependencies": {
70+ "eslint-plugin-prettier": "^3.1.0",
6671 "ganache-cli": "^6.2.1",
72+ "husky": "^2.4.0",
6773 "npm-run-all": "^4.1.3",
74+ "prettier": "^1.18.2",
6875 "serve": "10.1.1",
6976 "truffle": "4.1.11"
7077 },
src/Root.js+31−37
@@ -1,24 +1,24 @@
1−import 'babel-polyfill' // required for @ledgerhq/hw-transport-u2f
1+import 'babel-polyfill'; // required for @ledgerhq/hw-transport-u2f
22
3−import React from 'react'
4−import { get } from 'lodash'
3+import React from 'react';
4+import { get } from 'lodash';
55
6−import Bridge from './bridge/Bridge'
7−import Walletgen from './walletgen/Walletgen'
6+import Bridge from './bridge/Bridge';
7+import Walletgen from './walletgen/Walletgen';
88// import { AppNavigation } from './common/components/AppNavigation'
99
10−import './common/style/index.css'
11−import './bridge/style/index.css'
10+import './common/style/index.css';
11+import './bridge/style/index.css';
1212
1313class Root extends React.Component {
1414 constructor(props) {
15− super(props)
15+ super(props);
1616
1717 this.state = {
18− appView: "bridge"
19− }
18+ appView: 'bridge',
19+ };
2020
21− this.setView = this.setView.bind(this)
21+ this.setView = this.setView.bind(this);
2222 }
2323
2424 /*
@@ -36,57 +36,51 @@ class Root extends React.Component {
3636 */
3737
3838 isWalletgenStyle(styleSheet) {
39− return get(styleSheet, 'cssRules[0].selectorText', null) === "walletgen"
39+ return get(styleSheet, 'cssRules[0].selectorText', null) === 'walletgen';
4040 }
4141
4242 isBridgeStyle(styleSheet) {
43− return get(styleSheet, 'cssRules[0].selectorText', null) === "bridge"
43+ return get(styleSheet, 'cssRules[0].selectorText', null) === 'bridge';
4444 }
4545
4646 disableStylesheets(appView) {
4747 for (var i = 0; i < document.styleSheets.length; i++) {
48− let styleSheet = document.styleSheets[i]
49−
50− if (appView === "bridge") {
51− if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = true
52− if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = false
53− } else if (appView === "walletgen") {
54− if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = false
55− if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = true
48+ let styleSheet = document.styleSheets[i];
49+
50+ if (appView === 'bridge') {
51+ if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = true;
52+ if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = false;
53+ } else if (appView === 'walletgen') {
54+ if (this.isWalletgenStyle(styleSheet)) styleSheet.disabled = false;
55+ if (this.isBridgeStyle(styleSheet)) styleSheet.disabled = true;
5656 }
5757 }
5858 }
5959
6060 componentDidMount() {
61− this.disableStylesheets(this.state.appView)
61+ this.disableStylesheets(this.state.appView);
6262 }
6363
6464 setView(appView) {
65− this.disableStylesheets(appView)
65+ this.disableStylesheets(appView);
6666
6767 this.setState({
68− appView
69− })
68+ appView,
69+ });
7070 }
7171
7272 render() {
7373 const { appView } = this.state;
7474
7575 switch (appView) {
76− case "walletgen":
77− return <Walletgen
78− appView={appView}
79− setView={this.setView} />
80− case "bridge":
81− return <Bridge
82− appView={appView}
83− setView={this.setView} />
76+ case 'walletgen':
77+ return <Walletgen appView={appView} setView={this.setView} />;
78+ case 'bridge':
79+ return <Bridge appView={appView} setView={this.setView} />;
8480 default:
85− throw new Error(`Unknown appView ${appView}`)
81+ throw new Error(`Unknown appView ${appView}`);
8682 }
8783 }
8884}
8985
90−export {
91− Root
92−}
86+export { Root };
src/bridge/Bridge.js+174−176
@@ -1,60 +1,55 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import * as lodash from 'lodash'
3−import * as azimuth from 'azimuth-js'
4−import { Stack } from 'immutable'
5−import { CONTRACT_ADDRESSES } from './lib/contracts'
6−import React from 'react'
7−
8−import Web3 from 'web3'
9−import Footer from './components/Footer'
10−import Header from './components/Header'
11−import { Container, Row, Col } from './components/Base'
12−
13−import { ROUTE_NAMES, router } from './lib/router'
14−import { NETWORK_NAMES } from './lib/network'
1+import { Just, Nothing } from 'folktale/maybe';
2+import * as lodash from 'lodash';
3+import * as azimuth from 'azimuth-js';
4+import { Stack } from 'immutable';
5+import { CONTRACT_ADDRESSES } from './lib/contracts';
6+import React from 'react';
7+
8+import Web3 from 'web3';
9+import Footer from './components/Footer';
10+import Header from './components/Header';
11+import { Container, Row, Col } from './components/Base';
12+
13+import { ROUTE_NAMES, router } from './lib/router';
14+import { NETWORK_NAMES } from './lib/network';
1515import {
1616 WALLET_NAMES,
1717 DEFAULT_HD_PATH,
1818 walletFromMnemonic,
19− addressFromSecp256k1Public
20−} from './lib/wallet'
21−import { BRIDGE_ERROR } from './lib/error'
19+ addressFromSecp256k1Public,
20+} from './lib/wallet';
21+import { BRIDGE_ERROR } from './lib/error';
2222
23−const initWeb3 = (networkType) => {
23+const initWeb3 = networkType => {
2424 if (networkType === NETWORK_NAMES.MAINNET) {
25− const endpoint =
26− `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
25+ const endpoint = `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
2726
28− const provider = new Web3.providers.HttpProvider(endpoint)
29− const web3 = new Web3(provider)
30− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET)
31− return {web3: web3, contracts: contracts}
27+ const provider = new Web3.providers.HttpProvider(endpoint);
28+ const web3 = new Web3(provider);
29+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
30+ return { web3: web3, contracts: contracts };
3231 } else if (networkType === NETWORK_NAMES.LOCAL) {
33− const protocol =
34− process.env.NODE_ENV === 'development'
35− ? 'ws'
36− : 'wss'
37− const endpoint = `${protocol}://localhost:8545`
38− const provider = new Web3.providers.WebsocketProvider(endpoint)
39− const web3 = new Web3(provider)
40− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
41− return {web3: web3, contracts: contracts}
32+ const protocol = process.env.NODE_ENV === 'development' ? 'ws' : 'wss';
33+ const endpoint = `${protocol}://localhost:8545`;
34+ const provider = new Web3.providers.WebsocketProvider(endpoint);
35+ const web3 = new Web3(provider);
36+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
37+ return { web3: web3, contracts: contracts };
4238 }
43−}
39+};
4440
4541class Bridge extends React.Component {
46−
4742 constructor(props) {
48− super(props)
43+ super(props);
4944
5045 const networkType =
51− process.env.NODE_ENV === 'development'
52− ? NETWORK_NAMES.LOCAL
53− : NETWORK_NAMES.MAINNET
46+ process.env.NODE_ENV === 'development'
47+ ? NETWORK_NAMES.LOCAL
48+ : NETWORK_NAMES.MAINNET;
5449
5550 // Sidestepping full network selection to allow for point viewing,
5651 // but still respecting the networkType
57− const { web3, contracts } = initWeb3(networkType)
52+ const { web3, contracts } = initWeb3(networkType);
5853
5954 // NB (jtobin):
6055 //
@@ -71,7 +66,7 @@ class Bridge extends React.Component {
7166
7267 this.state = {
7368 // routes
74− routeCrumbs: Stack([ ROUTE_NAMES.LANDING ]),
69+ routeCrumbs: Stack([ROUTE_NAMES.LANDING]),
7570 routeData: {},
7671 // network
7772 networkType: networkType,
@@ -90,129 +85,128 @@ class Bridge extends React.Component {
9085 pointCache: {},
9186 // txn
9287 txnHashCursor: Nothing(),
93− txnConfirmations: {}
94− }
95−
96− this.pushRoute = this.pushRoute.bind(this)
97− this.popRoute = this.popRoute.bind(this)
98− this.skipRoute = this.skipRoute.bind(this)
99− this.setNetworkType = this.setNetworkType.bind(this)
100− this.setNetwork = this.setNetwork.bind(this)
101− this.setWalletType = this.setWalletType.bind(this)
102− this.setWallet = this.setWallet.bind(this)
103− this.setWalletHdPath = this.setWalletHdPath.bind(this)
104− this.setUrbitWallet = this.setUrbitWallet.bind(this)
105− this.setAuthMnemonic = this.setAuthMnemonic.bind(this)
106− this.setPointCursor = this.setPointCursor.bind(this)
107− this.setTxnHashCursor = this.setTxnHashCursor.bind(this)
108− this.setTxnConfirmations = this.setTxnConfirmations.bind(this)
109− this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this)
110− this.addToPointCache = this.addToPointCache.bind(this)
88+ txnConfirmations: {},
89+ };
90+
91+ this.pushRoute = this.pushRoute.bind(this);
92+ this.popRoute = this.popRoute.bind(this);
93+ this.skipRoute = this.skipRoute.bind(this);
94+ this.setNetworkType = this.setNetworkType.bind(this);
95+ this.setNetwork = this.setNetwork.bind(this);
96+ this.setWalletType = this.setWalletType.bind(this);
97+ this.setWallet = this.setWallet.bind(this);
98+ this.setWalletHdPath = this.setWalletHdPath.bind(this);
99+ this.setUrbitWallet = this.setUrbitWallet.bind(this);
100+ this.setAuthMnemonic = this.setAuthMnemonic.bind(this);
101+ this.setPointCursor = this.setPointCursor.bind(this);
102+ this.setTxnHashCursor = this.setTxnHashCursor.bind(this);
103+ this.setTxnConfirmations = this.setTxnConfirmations.bind(this);
104+ this.setNetworkSeedCache = this.setNetworkSeedCache.bind(this);
105+ this.addToPointCache = this.addToPointCache.bind(this);
111106 }
112107
113108 componentDidMount() {
114109 window.history.pushState(null, null, null);
115110
116− window.onpopstate = (e) => {
111+ window.onpopstate = e => {
117112 window.history.pushState(null, null, null);
118113 this.popRoute();
119− }
114+ };
120115
121− // NB (jtobin):
122− //
123− // If running in development, the following can be tweaked to get you set
124− // up with a desirable initial state.
125−
126− // if (process.env.NODE_ENV === 'development') {
127− //
128− // const socket = 'ws://localhost:8545'
129− // const provider = new Web3.providers.WebsocketProvider(socket)
130− // const web3 = new Web3(provider)
131− // const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
132− //
133− // const mnemonic = process.env.REACT_APP_DEV_MNEMONIC
134− // const hdpath = process.env.REACT_APP_HD_PATH
135− //
136− // this.setState({
137− // routeCrumbs: Stack([
138− // // ROUTE_NAMES.CREATE_GALAXY,
139− // // ROUTE_NAMES.SHIP,
140− // ROUTE_NAMES.SHIPS,
141− // // ROUTE_NAMES.MNEMONIC,
142− // ROUTE_NAMES.WALLET,
143− // ROUTE_NAMES.NETWORK,
144− // ROUTE_NAMES.LANDING
145− // ]),
146− // networkType: NETWORK_NAMES.LOCAL,
147− // pointCursor: Just(0),
148− // web3: Just(web3),
149− // contracts: Just(contracts),
150− // walletType: WALLET_NAMES.MNEMONIC,
151− // wallet: walletFromMnemonic(mnemonic, hdpath),
152− // urbitWallet: Nothing(),
153− // authMnemonic: Just('benefit crew supreme gesture quantum web media hazard theory mercy wing kitten')
154− // })
155− // }
156−}
116+ // NB (jtobin):
117+ //
118+ // If running in development, the following can be tweaked to get you set
119+ // up with a desirable initial state.
120+
121+ // if (process.env.NODE_ENV === 'development') {
122+ //
123+ // const socket = 'ws://localhost:8545'
124+ // const provider = new Web3.providers.WebsocketProvider(socket)
125+ // const web3 = new Web3(provider)
126+ // const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
127+ //
128+ // const mnemonic = process.env.REACT_APP_DEV_MNEMONIC
129+ // const hdpath = process.env.REACT_APP_HD_PATH
130+ //
131+ // this.setState({
132+ // routeCrumbs: Stack([
133+ // // ROUTE_NAMES.CREATE_GALAXY,
134+ // // ROUTE_NAMES.SHIP,
135+ // ROUTE_NAMES.SHIPS,
136+ // // ROUTE_NAMES.MNEMONIC,
137+ // ROUTE_NAMES.WALLET,
138+ // ROUTE_NAMES.NETWORK,
139+ // ROUTE_NAMES.LANDING
140+ // ]),
141+ // networkType: NETWORK_NAMES.LOCAL,
142+ // pointCursor: Just(0),
143+ // web3: Just(web3),
144+ // contracts: Just(contracts),
145+ // walletType: WALLET_NAMES.MNEMONIC,
146+ // wallet: walletFromMnemonic(mnemonic, hdpath),
147+ // urbitWallet: Nothing(),
148+ // authMnemonic: Just('benefit crew supreme gesture quantum web media hazard theory mercy wing kitten')
149+ // })
150+ // }
151+ }
157152
158153 pushRoute(symbol, routeData) {
159154 if (lodash.includes(ROUTE_NAMES, symbol)) {
160155 this.setState((state, _) => ({
161156 routeCrumbs: state.routeCrumbs.push(symbol),
162− routeData: routeData
157+ routeData: routeData,
163158 }));
164159
165160 // Scroll to top of page with each route transition.
166161 window.scrollTo(0, 0);
167−
168162 } else {
169− throw BRIDGE_ERROR.INVALID_ROUTE
163+ throw BRIDGE_ERROR.INVALID_ROUTE;
170164 }
171165 }
172166
173167 skipRoute(len) {
174168 this.setState((state, _) => ({
175− routeCrumbs: state.routeCrumbs.skip(len)
176− }))
169+ routeCrumbs: state.routeCrumbs.skip(len),
170+ }));
177171 }
178172
179173 popRoute() {
180174 this.setState((state, _) => ({
181− routeCrumbs: state.routeCrumbs.shift()
182− }))
175+ routeCrumbs: state.routeCrumbs.shift(),
176+ }));
183177 }
184178
185179 setNetworkType(symbol) {
186180 if (lodash.includes(NETWORK_NAMES, symbol)) {
187181 this.setState({
188− networkType: symbol
189− })
182+ networkType: symbol,
183+ });
190184 } else {
191− throw BRIDGE_ERROR.INVALID_NETWORK_TYPE
185+ throw BRIDGE_ERROR.INVALID_NETWORK_TYPE;
192186 }
193187 }
194188
195189 setNetworkSeedCache(networkSeed, revision) {
196190 this.setState({
197191 networkSeedCache: networkSeed,
198− networkRevisionCache: revision
199− })
192+ networkRevisionCache: revision,
193+ });
200194 }
201195
202196 setNetwork(web3, contracts) {
203197 this.setState({
204198 web3: web3,
205− contracts: contracts
206− })
199+ contracts: contracts,
200+ });
207201 }
208202
209203 setWalletType(symbol) {
210204 if (lodash.includes(WALLET_NAMES, symbol)) {
211205 this.setState({
212− walletType: symbol
213− })
206+ walletType: symbol,
207+ });
214208 } else {
215− throw BRIDGE_ERROR.INVALID_WALLET_TYPE
209+ throw BRIDGE_ERROR.INVALID_WALLET_TYPE;
216210 }
217211 }
218212
@@ -225,7 +219,7 @@ class Bridge extends React.Component {
225219 }
226220
227221 setWalletHdPath(walletHdPath) {
228− this.setState({ walletHdPath })
222+ this.setState({ walletHdPath });
229223 }
230224
231225 // also sets wallet to ownership address
@@ -243,34 +237,38 @@ class Bridge extends React.Component {
243237 return wal;
244238 });
245239 }
246− this.setState({ urbitWallet, wallet })
240+ this.setState({ urbitWallet, wallet });
247241 }
248242
249243 setAuthMnemonic(authMnemonic) {
250− this.setState({ authMnemonic })
244+ this.setState({ authMnemonic });
251245 }
252246
253247 setPointCursor(pointCursor) {
254− this.setState({ pointCursor })
248+ this.setState({ pointCursor });
255249 }
256250
257251 addToPointCache(entry) {
258252 this.setState((state, _) => ({
259− pointCache: Object.assign(state.pointCache, entry)
260− }))
253+ pointCache: Object.assign(state.pointCache, entry),
254+ }));
261255 }
262256
263257 setTxnHashCursor(txnHashCursor) {
264− this.setState({ txnHashCursor })
258+ this.setState({ txnHashCursor });
265259 }
266260
267261 setTxnConfirmations(txnHash, txnConfirmations) {
268262 this.setState((prevState, _) => {
269263 return prevState.routeCrumbs.peek() === ROUTE_NAMES.SENT_TRANSACTION
270− ? {txnConfirmations: { ...prevState.txnConfirmations,
271− [txnHash]: txnConfirmations}}
272− : null
273− })
264+ ? {
265+ txnConfirmations: {
266+ ...prevState.txnConfirmations,
267+ [txnHash]: txnConfirmations,
268+ },
269+ }
270+ : null;
271+ });
274272 }
275273
276274 render() {
@@ -290,87 +288,87 @@ class Bridge extends React.Component {
290288 txnHashCursor,
291289 txnConfirmations,
292290 web3,
293− contracts
294− } = this.state
291+ contracts,
292+ } = this.state;
295293
296− const View = router(routeCrumbs.peek())
294+ const View = router(routeCrumbs.peek());
297295
298296 // For the invite acceptance flow, widen the screen to use the full
299297 // container, and hide the breadcrumbs
300298 const colClass = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
301− ? 'col-md-12'
302− : 'col-md-offset-1 col-md-10'
299+ ? 'col-md-12'
300+ : 'col-md-offset-1 col-md-10';
303301
304302 const colStyle = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
305− ? {}
306− : {maxWidth: '620px'}
303+ ? {}
304+ : { maxWidth: '620px' };
307305
308306 const showCrumbs = routeCrumbs.contains(ROUTE_NAMES.INVITE_TICKET)
309− ? false
310− : true
307+ ? false
308+ : true;
311309
312310 return (
313311 <Container>
314312 <Row>
315313 <Col className={colClass} style={colStyle}>
316314 <Header
317− routeCrumbs={ routeCrumbs }
318− skipRoute={ this.skipRoute }
319− networkType={ networkType }
320− wallet={ wallet }
321− showCrumbs={ showCrumbs }
322− pointCursor={ pointCursor } />
323−
324− <Row className={ 'row wrapper' }>
325−
315+ routeCrumbs={routeCrumbs}
316+ skipRoute={this.skipRoute}
317+ networkType={networkType}
318+ wallet={wallet}
319+ showCrumbs={showCrumbs}
320+ pointCursor={pointCursor}
321+ />
322+
323+ <Row className={'row wrapper'}>
326324 <View
327325 // router
328− pushRoute={ this.pushRoute }
329− popRoute={ this.popRoute }
330− routeData={ routeData }
326+ pushRoute={this.pushRoute}
327+ popRoute={this.popRoute}
328+ routeData={routeData}
331329 // network
332− setNetworkType={ this.setNetworkType }
333− setNetwork={ this.setNetwork }
334− networkType={ networkType }
335− web3={ web3 }
336− contracts={ contracts }
330+ setNetworkType={this.setNetworkType}
331+ setNetwork={this.setNetwork}
332+ networkType={networkType}
333+ web3={web3}
334+ contracts={contracts}
337335 // wallet
338− setWalletType={ this.setWalletType }
339− setWalletHdPath={ this.setWalletHdPath }
340− setWallet={ this.setWallet }
341− walletType={ walletType }
342− walletHdPath={ walletHdPath }
343− wallet={ wallet }
336+ setWalletType={this.setWalletType}
337+ setWalletHdPath={this.setWalletHdPath}
338+ setWallet={this.setWallet}
339+ walletType={walletType}
340+ walletHdPath={walletHdPath}
341+ wallet={wallet}
344342 // urbit wallet
345− urbitWallet={ urbitWallet }
346− setUrbitWallet={ this.setUrbitWallet }
347− authMnemonic={ authMnemonic }
348− setAuthMnemonic={ this.setAuthMnemonic }
343+ urbitWallet={urbitWallet}
344+ setUrbitWallet={this.setUrbitWallet}
345+ authMnemonic={authMnemonic}
346+ setAuthMnemonic={this.setAuthMnemonic}
349347 // point
350− setPointCursor={ this.setPointCursor }
351− addToPointCache={ this.addToPointCache }
352− pointCursor={ pointCursor }
353− pointCache={ pointCache }
354− networkSeedCache= { networkSeedCache }
355− networkRevisionCache={ networkRevisionCache }
356− setNetworkSeedCache= { this.setNetworkSeedCache }
348+ setPointCursor={this.setPointCursor}
349+ addToPointCache={this.addToPointCache}
350+ pointCursor={pointCursor}
351+ pointCache={pointCache}
352+ networkSeedCache={networkSeedCache}
353+ networkRevisionCache={networkRevisionCache}
354+ setNetworkSeedCache={this.setNetworkSeedCache}
357355 // txn
358− onSent={ this.setTxnHashCursor }
359− setTxnHashCursor={ this.setTxnHashCursor }
360− txnHashCursor={ txnHashCursor }
361− setTxnConfirmations={ this.setTxnConfirmations }
362− txnConfirmations={ txnConfirmations } />
356+ onSent={this.setTxnHashCursor}
357+ setTxnHashCursor={this.setTxnHashCursor}
358+ txnHashCursor={txnHashCursor}
359+ setTxnConfirmations={this.setTxnConfirmations}
360+ txnConfirmations={txnConfirmations}
361+ />
363362
364363 <div className={'push'} />
365−
366364 </Row>
367365
368366 <Footer />
369367 </Col>
370368 </Row>
371369 </Container>
372− )
370+ );
373371 }
374372}
375373
376−export default Bridge
374+export default Bridge;
src/bridge/components/Anchor.js+13−18
@@ -1,33 +1,28 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−import ProtoAnchor from './ProtoAnchor'
5−
4+import ProtoAnchor from './ProtoAnchor';
65
76const Anchor = props => {
8−
97 return (
10− <ProtoAnchor
11− {...props}
12− prop-disabled={`${props['prop-disabled']}`}>
13− { props.children }
8+ <ProtoAnchor {...props} prop-disabled={`${props['prop-disabled']}`}>
9+ {props.children}
1410 </ProtoAnchor>
15− )
16−}
11+ );
12+};
1713
1814Anchor.propTypes = {
19− 'prop-disabled': PropTypes.bool,
20− 'prop-color': PropTypes.string,
21− 'prop-size': PropTypes.string,
22− 'prop-type': PropTypes.string,
15+ 'prop-disabled': PropTypes.bool,
16+ 'prop-color': PropTypes.string,
17+ 'prop-size': PropTypes.string,
18+ 'prop-type': PropTypes.string,
2319};
2420
2521Anchor.defaultProps = {
26− 'prop-disabled': false,
27− 'prop-color': 'black',
28− 'prop-size': 'md',
29− 'prop-type': 'link',
22+ 'prop-disabled': false,
23+ 'prop-color': 'black',
24+ 'prop-size': 'md',
25+ 'prop-type': 'link',
3026};
3127
32−
3328export default Anchor;
src/bridge/components/Base.js+40−87
@@ -9,12 +9,10 @@ import {
99 validateTicket,
1010 validateNetworkKey,
1111 validateNetworkSeed,
12− validateShard
12+ validateShard,
1313} from '../lib/validators';
1414
15−import {
16− prependSig,
17−} from '../lib/transformers';
15+import { prependSig } from '../lib/transformers';
1816
1917// Buttons
2018import Button from './Button';
@@ -25,7 +23,7 @@ import Anchor from './Anchor';
2523// import ToggleButton from './ToggleButton';
2624
2725// Input
28−import Form from './Form'
26+import Form from './Form';
2927import Label from './Label';
3028import InnerLabel from './InnerLabel';
3129import InputCaption from './InputCaption';
@@ -59,120 +57,84 @@ import H2 from './H2';
5957import H3 from './H3';
6058import H4 from './H4';
6159import P from './P';
62−import Code from './Code'
63−import Warning from './Warning'
60+import Code from './Code';
61+import Warning from './Warning';
6462
6563// Misc
6664import ValidatedSigil from './ValidatedSigil';
6765import ValidatedBlockie from './ValidatedBlockie';
68−import Chevron from './icons/Chevron'
69−import ShowBlockie from './ShowBlockie'
70−import Passport from './Passport'
66+import Chevron from './icons/Chevron';
67+import ShowBlockie from './ShowBlockie';
68+import Passport from './Passport';
7169
7270// HOC validated form components
7371
7472const RequiredInput = advancedInput({
7573 WrappedComponent: Input,
76− validators: [
77− validateNotEmpty,
78− ],
74+ validators: [validateNotEmpty],
7975});
8076
81−
8277const NetworkKeyInput = advancedInput({
8378 WrappedComponent: Input,
84− validators: [
85− validateNotEmpty,
86− validateNetworkKey
87− ],
79+ validators: [validateNotEmpty, validateNetworkKey],
8880});
8981
90−
9182const NetworkSeedInput = advancedInput({
9283 WrappedComponent: Input,
93− validators: [
94− validateNotEmpty,
95− validateNetworkSeed
96− ],
84+ validators: [validateNotEmpty, validateNetworkSeed],
9785});
9886
99−
10087const AddressInput = advancedInput({
10188 WrappedComponent: Input,
102− validators: [
103− validateEthereumAddress,
104− validateNotEmpty,
105− ],
89+ validators: [validateEthereumAddress, validateNotEmpty],
10690});
10791
108−
10992const MnemonicInput = advancedInput({
11093 WrappedComponent: Textarea,
111− validators: [
112− validateMnemonic,
113− validateNotEmpty,
114− ],
94+ validators: [validateMnemonic, validateNotEmpty],
11595});
11696
11797const GalaxyInput = advancedInput({
11898 WrappedComponent: Input,
119− validators: [
120− validateGalaxy,
121− validateNotEmpty,
122− ],
123− transformers: [
124− prependSig,
125− ]
99+ validators: [validateGalaxy, validateNotEmpty],
100+ transformers: [prependSig],
126101});
127102
128103const PointInput = advancedInput({
129104 WrappedComponent: Input,
130− validators: [
131− validatePoint,
132− validateNotEmpty,
133− ],
134− transformers: [
135− prependSig,
136− ]
105+ validators: [validatePoint, validateNotEmpty],
106+ transformers: [prependSig],
137107});
138108
139109const TicketInput = advancedInput({
140110 WrappedComponent: Input,
141− validators: [
142− validateTicket,
143− validateNotEmpty,
144− ],
145− transformers: [
146− prependSig,
147− ]
111+ validators: [validateTicket, validateNotEmpty],
112+ transformers: [prependSig],
148113});
149114
150−const VerifyTicketInput = matchingTicket => advancedInput({
151− WrappedComponent: Input,
152− validators: [
153− validateTicket,
154− validateNotEmpty,
155−
156− // TODO: This should be in /lib/validators, but we'd need a way for
157− // validators to compare against dynamic values
158− m => simpleValidatorWrapper({
159− prevMessage: m,
160− validator: d => d === matchingTicket,
161− errorMessage: 'This does not match the provided master ticket'
162− })
163− ],
164− transformers: [
165− prependSig,
166− ]
167−});
115+const VerifyTicketInput = matchingTicket =>
116+ advancedInput({
117+ WrappedComponent: Input,
118+ validators: [
119+ validateTicket,
120+ validateNotEmpty,
121+
122+ // TODO: This should be in /lib/validators, but we'd need a way for
123+ // validators to compare against dynamic values
124+ m =>
125+ simpleValidatorWrapper({
126+ prevMessage: m,
127+ validator: d => d === matchingTicket,
128+ errorMessage: 'This does not match the provided master ticket',
129+ }),
130+ ],
131+ transformers: [prependSig],
132+ });
168133
169134const ShardInput = advancedInput({
170135 WrappedComponent: Input,
171− validators: [
172− validateShard
173− ],
174− transformers: [
175− ]
136+ validators: [validateShard],
137+ transformers: [],
176138});
177139
178140export {
@@ -181,7 +143,6 @@ export {
181143 UploadButton,
182144 Anchor,
183145 InputCaption,
184−
185146 Form,
186147 Label,
187148 Input,
@@ -197,29 +158,22 @@ export {
197158 RequiredInput,
198159 NetworkKeyInput,
199160 NetworkSeedInput,
200−
201161 advancedInput,
202−
203162 ValidatedSigil,
204163 ValidatedBlockie,
205164 ShowBlockie,
206−
207165 DropdownItem,
208166 DropdownDivider,
209167 InnerLabelDropdown,
210−
211168 HorizontalSelector,
212−
213169 Breadcrumb,
214170 BreadcrumbItem,
215−
216171 Container,
217172 Col,
218173 Row,
219174 // FlexRow,
220175 // FlexCol,
221176 Spacing,
222−
223177 H1,
224178 H2,
225179 H3,
@@ -227,7 +181,6 @@ export {
227181 P,
228182 Code,
229183 Warning,
230−
231184 Chevron,
232− Passport
185+ Passport,
233186};
src/bridge/components/Blockie.js+13−8
@@ -1,12 +1,17 @@
1−import React from 'react'
2−import makeBlockie from 'ethereum-blockies-base64'
1+import React from 'react';
2+import makeBlockie from 'ethereum-blockies-base64';
33
4−const isValidAddress = (address) =>
5− /^0x[0-9a-fA-F]{40}$/.test(address)
4+const isValidAddress = address => /^0x[0-9a-fA-F]{40}$/.test(address);
65
76const Blockie = props =>
8− isValidAddress(props.address)
9− ? <img className={`s-10 ${props.className}`} alt={ props.address } src={ makeBlockie(props.address) } />
10− : <div className={`s-10 ${props.className}`} />
7+ isValidAddress(props.address) ? (
8+ <img
9+ className={`s-10 ${props.className}`}
10+ alt={props.address}
11+ src={makeBlockie(props.address)}
12+ />
13+ ) : (
14+ <div className={`s-10 ${props.className}`} />
15+ );
1116
12−export default Blockie
17+export default Blockie;
src/bridge/components/Breadcrumb.js+4−7
@@ -1,12 +1,9 @@
11import React from 'react';
22
3−const Breadcrumb = ({ disabled, onClick, className, children }) =>
4− <div
5− className={`flex ${className}`}
6− onClick={onClick}
7− disabled={disabled}
8− >
9− { children }
3+const Breadcrumb = ({ disabled, onClick, className, children }) => (
4+ <div className={`flex ${className}`} onClick={onClick} disabled={disabled}>
5+ {children}
106 </div>
7+);
118
129export default Breadcrumb;
src/bridge/components/BreadcrumbItem.js+4−7
@@ -1,12 +1,9 @@
11import React from 'react';
22
3−const BreadcrumbItem = ({ disabled, onClick, className, children }) =>
4− <div
5− className={`${className}`}
6− onClick={onClick}
7− disabled={disabled}
8− >
9− { children }
3+const BreadcrumbItem = ({ disabled, onClick, className, children }) => (
4+ <div className={`${className}`} onClick={onClick} disabled={disabled}>
5+ {children}
106 </div>
7+);
118
129export default BreadcrumbItem;
src/bridge/components/Button.js+3−12
@@ -1,17 +1,10 @@
11import React from 'react';
22import PropTypes from 'prop-types';
3−import ProtoButton from './ProtoButton'
4−
3+import ProtoButton from './ProtoButton';
54
65const Button = props => {
7− return (
8− <ProtoButton
9− {...props}>
10− { props.children }
11− </ProtoButton>
12− )
13−}
14−
6+ return <ProtoButton {...props}>{props.children}</ProtoButton>;
7+};
158
169Button.propTypes = {
1710 'prop-color': PropTypes.string,
@@ -19,12 +12,10 @@ Button.propTypes = {
1912 'prop-type': PropTypes.string,
2013};
2114
22−
2315Button.defaultProps = {
2416 'prop-color': 'blue',
2517 'prop-size': 'md',
2618 'prop-type': 'solid',
2719};
2820
29−
3021export default Button;
src/bridge/components/Checkbox.js+22−19
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
22
33const CheckBox = ({
44 name,
@@ -10,38 +10,41 @@ const CheckBox = ({
1010 state,
1111 children,
1212 childContainerClassname,
13− disabled
13+ disabled,
1414}) => {
15−
16− const isCheckedClassname = state === true
17− ? 'cb-on'
18− : 'cb-off';
19−
15+ const isCheckedClassname = state === true ? 'cb-on' : 'cb-off';
2016
2117 return (
2218 <div className={`cb flex items-center ${isCheckedClassname} ${className}`}>
2319 <label className={'s-6 m-0 mr-4 rel outline-blue'}>
24−
2520 <input
26− disabled={ disabled }
21+ disabled={disabled}
2722 name={name}
2823 id={id}
2924 type={'checkbox'}
3025 className={'none'}
3126 checked={state}
32− onChange={onChange} />
33−
34− <svg width='24' height='24' viewBox='0 0 24 24' fill='none' xmlns='http://www.w3.org/2000/svg'>
35− <path d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z' transform='translate(4 11) rotate(-45)' fill='white'/>
27+ onChange={onChange}
28+ />
29+
30+ <svg
31+ width="24"
32+ height="24"
33+ viewBox="0 0 24 24"
34+ fill="none"
35+ xmlns="http://www.w3.org/2000/svg">
36+ <path
37+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
38+ transform="translate(4 11) rotate(-45)"
39+ fill="white"
40+ />
3641 </svg>
37−
3842 </label>
3943 <div className={`user-select-none ${childContainerClassname} mt-1`}>
40− { children }
44+ {children}
4145 </div>
4246 </div>
43− )
44−}
45−
47+ );
48+};
4649
47−export default CheckBox
50+export default CheckBox;
src/bridge/components/CheckboxButton.js+28−35
@@ -1,62 +1,55 @@
1−import React from 'react'
1+import React from 'react';
22import PropTypes from 'prop-types';
3−import ProtoButton from './ProtoButton'
3+import ProtoButton from './ProtoButton';
44
55const CheckboxButton = props => {
6+ const onOff =
7+ props.state === true
8+ ? 'cb-on br-blue bg-blue outline-blue'
9+ : 'bg-transparent br-black outline-blue';
610
7− const onOff = props.state === true
8− ? 'cb-on br-blue bg-blue outline-blue'
9− : 'bg-transparent br-black outline-blue';
10−
11− const checkmarkState = props.state === true ? 'o-100' : 'o-0'
12−
13− const classes = props.disabled ? 'br-gray-30 bg-gray-10' : onOff
11+ const checkmarkState = props.state === true ? 'o-100' : 'o-0';
1412
13+ const classes = props.disabled ? 'br-gray-30 bg-gray-10' : onOff;
1514
1615 return (
1716 <div
1817 className={`flex items-center ${props.className}`}
19− style={{cursor: 'pointer', ...props.style}}
18+ style={{ cursor: 'pointer', ...props.style }}
2019 onClick={props.onToggle}>
21−
2220 <ProtoButton
2321 // {...getDomProps(props)}
2422 {...props}
25− className={ `s-7 p-0 o-blue mr-4 bs-solid bw-2 ${classes}` }>
26−
23+ className={`s-7 p-0 o-blue mr-4 bs-solid bw-2 ${classes}`}>
2724 <svg
2825 className={checkmarkState}
29− width='24'
30− height='24'
31− viewBox='0 0 24 24'
32− fill='none'
33− xmlns='http://www.w3.org/2000/svg'>
26+ width="24"
27+ height="24"
28+ viewBox="0 0 24 24"
29+ fill="none"
30+ xmlns="http://www.w3.org/2000/svg">
3431 <path
35− d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z'
36− transform='translate(6, 13) rotate(-45)'
37− fill='white'/>
32+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
33+ transform="translate(6, 13) rotate(-45)"
34+ fill="white"
35+ />
3836 </svg>
39−
4037 </ProtoButton>
4138
42− <div>{ props.label }</div>
43− { props.children }
44−
39+ <div>{props.label}</div>
40+ {props.children}
4541 </div>
46− )
47−}
48−
42+ );
43+};
4944
5045CheckboxButton.propTypes = {
51− state: PropTypes.bool,
52− className: PropTypes.string,
46+ state: PropTypes.bool,
47+ className: PropTypes.string,
5348};
5449
55−
5650CheckboxButton.defaultProps = {
57− state: false,
58− className: '',
51+ state: false,
52+ className: '',
5953};
6054
61−
62−export default CheckboxButton
55+export default CheckboxButton;
src/bridge/components/Code.js+4−7
@@ -1,16 +1,15 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const Code = ({ className, children, style, measure }) => {
5−
65 // const s = getStyles(styleManifest, measure)
76
87 return (
98 <pre className={`flex ${className}`} style={style}>
10− { children }
9+ {children}
1110 </pre>
12− )
13−}
11+ );
12+};
1413
1514// const getStyles = (sm, m) => sm[m];
1615//
@@ -26,12 +25,10 @@ Code.propTypes = {
2625 style: PropTypes.object,
2726};
2827
29−
3028Code.defaultProps = {
3129 className: '',
3230 style: {},
3331 measure: 'md',
3432};
3533
36−
3734export default Code;
src/bridge/components/Col.js+3−9
@@ -1,17 +1,12 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
5−const Col = ({
6− className,
7− children,
8− style,
9−}) => {
4+const Col = ({ className, children, style }) => {
105 return (
116 <div className={`col- ${className}`} style={style}>
12− { children }
7+ {children}
138 </div>
14− )
9+ );
1510};
1611
1712Col.propTypes = {
@@ -25,5 +20,4 @@ Col.defaultProps = {
2520 style: {},
2621};
2722
28−
2923export default Col;
src/bridge/components/Container.js+3−9
@@ -1,17 +1,12 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
5−const Container = ({
6− className,
7− children,
8− style,
9−}) => {
4+const Container = ({ className, children, style }) => {
105 return (
116 <main className={`container ${className}`} style={style}>
12− { children }
7+ {children}
138 </main>
14− )
9+ );
1510};
1611
1712Container.propTypes = {
@@ -25,5 +20,4 @@ Container.defaultProps = {
2520 style: {},
2621};
2722
28−
2923export default Container;
src/bridge/components/DropdownDivider.js+1−2
@@ -1,12 +1,11 @@
11import React from 'react';
22
3−
43const DropdownDivider = props => {
54 return (
65 <div className={`br-gray-50 bs-solid bw-0-2-0-2 ${props.className}`}>
76 <div className={`block bs-solid bw-2-0-0-0 br-gray-30 mv-2`} />
87 </div>
9− )
8+ );
109};
1110
1211export default DropdownDivider;
src/bridge/components/DropdownItem.js+2−5
@@ -1,9 +1,7 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
54const DropdownItem = props => {
6−
75 const _disabled = props.disabled
86 ? 'gray-50'
97 : 'h-br-blue br-gray-50 h-bg-blue h-white';
@@ -14,9 +12,9 @@ const DropdownItem = props => {
1412 style={props.style}
1513 className={`h-10 m-0 p-0 ph-2 bg-white flex align-center items-start bs-solid brw-2 blw-2 btw-0 bbw-0 sans ${_disabled} ${props.className}`}
1614 onClick={props.onClick}>
17− { props.children }
15+ {props.children}
1816 </button>
19− )
17+ );
2018};
2119
2220DropdownItem.propTypes = {
@@ -27,7 +25,6 @@ DropdownItem.propTypes = {
2725 style: PropTypes.object,
2826};
2927
30−
3128DropdownItem.defaultProps = {
3229 className: '',
3330 onClick: () => {},
src/bridge/components/FlexCol.js+3−9
@@ -1,17 +1,12 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
5−const FlexCol = ({
6− className,
7− children,
8− style,
9−}) => {
4+const FlexCol = ({ className, children, style }) => {
105 return (
116 <div className={`flex flex-column flex-1 ${className}`} style={style}>
12− { children }
7+ {children}
138 </div>
14− )
9+ );
1510};
1611
1712FlexCol.propTypes = {
@@ -25,5 +20,4 @@ FlexCol.defaultProps = {
2520 style: {},
2621};
2722
28−
2923export default FlexCol;
src/bridge/components/FlexRow.js+5−14
@@ -1,26 +1,17 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
5−const FlexRow = ({
6− className,
7− children,
8−}) => {
9− return (
10− <div className={`flex ${className}`}>
11− { children }
12− </div>
13− )
4+const FlexRow = ({ className, children }) => {
5+ return <div className={`flex ${className}`}>{children}</div>;
146};
157
168FlexRow.propTypes = {
179 className: PropTypes.string,
1810 children: PropTypes.node,
19−}
11+};
2012
2113FlexRow.defaultProps = {
2214 className: '',
23−}
24−
15+};
2516
26−export default FlexRow
17+export default FlexRow;
src/bridge/components/Footer.js+7−15
@@ -1,23 +1,15 @@
1−import React from 'react'
1+import React from 'react';
22
3−import { version } from '../../../package.json'
3+import { version } from '../../../package.json';
44
5−const Footer = (props) =>
5+const Footer = props => (
66 <footer className={'row h-13 mt-20'}>
7−
8− <div className={'col-md-6 text-mono'}>
9− { 'v' + version }
10− </div>
7+ <div className={'col-md-6 text-mono'}>{'v' + version}</div>
118
129 <div className={'col-md-3'}>
13− <a href='https://github.com/urbit/bridge'>
14− { 'GitHub' }
15− </a>
10+ <a href="https://github.com/urbit/bridge">{'GitHub'}</a>
1611 </div>
17−
1812 </footer>
13+);
1914
20−
21−
22−
23−export default Footer
15+export default Footer;
src/bridge/components/Form.js+3−9
@@ -1,17 +1,12 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
5−const Form = ({
6− className,
7− children,
8− style,
9−}) => {
4+const Form = ({ className, children, style }) => {
105 return (
116 <form className={`${className}`} style={style}>
12− { children }
7+ {children}
138 </form>
14− )
9+ );
1510};
1611
1712Form.propTypes = {
@@ -25,5 +20,4 @@ Form.defaultProps = {
2520 style: {},
2621};
2722
28−
2923export default Form;
src/bridge/components/H1.js+7−8
@@ -1,14 +1,15 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const H1 = ({ className, children, style }) => {
55 return (
6− <h1 className={`mt-12 pt-6 mb-4 text-700 measure-md ${className}`} style={style}>
7− { children }
6+ <h1
7+ className={`mt-12 pt-6 mb-4 text-700 measure-md ${className}`}
8+ style={style}>
9+ {children}
810 </h1>
9− )
10−}
11−
11+ );
12+};
1213
1314H1.propTypes = {
1415 className: PropTypes.string,
@@ -16,11 +17,9 @@ H1.propTypes = {
1617 style: PropTypes.object,
1718};
1819
19−
2020H1.defaultProps = {
2121 className: '',
2222 style: {},
2323};
2424
25−
2625export default H1;
src/bridge/components/H2.js+4−7
@@ -1,14 +1,13 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const H2 = ({ className, children, style }) => {
55 return (
66 <h2 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
88 </h2>
9− )
10−}
11−
9+ );
10+};
1211
1312H2.propTypes = {
1413 className: PropTypes.string,
@@ -16,11 +15,9 @@ H2.propTypes = {
1615 style: PropTypes.object,
1716};
1817
19−
2018H2.defaultProps = {
2119 className: '',
2220 style: {},
2321};
2422
25−
2623export default H2;
src/bridge/components/H3.js+4−7
@@ -1,14 +1,13 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const H3 = ({ className, children, style }) => {
55 return (
66 <h3 className={`mt-8 pt-3 mb-4 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
88 </h3>
9− )
10−}
11−
9+ );
10+};
1211
1312H3.propTypes = {
1413 className: PropTypes.string,
@@ -16,11 +15,9 @@ H3.propTypes = {
1615 style: PropTypes.object,
1716};
1817
19−
2018H3.defaultProps = {
2119 className: '',
2220 style: {},
2321};
2422
25−
2623export default H3;
src/bridge/components/H4.js+4−7
@@ -1,14 +1,13 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const H4 = ({ className, children, style }) => {
55 return (
66 <h4 className={`mt-2 pt-1 mb-2 measure-md ${className}`} style={style}>
7− { children }
7+ {children}
88 </h4>
9− )
10−}
11−
9+ );
10+};
1211
1312H4.propTypes = {
1413 className: PropTypes.string,
@@ -16,11 +15,9 @@ H4.propTypes = {
1615 style: PropTypes.object,
1716};
1817
19−
2018H4.defaultProps = {
2119 className: '',
2220 style: {},
2321};
2422
25−
2623export default H4;
src/bridge/components/Header.js+44−40
@@ -1,54 +1,58 @@
1−import React from 'react'
2−import { Button, Chevron } from '../components/Base'
1+import React from 'react';
2+import { Button, Chevron } from '../components/Base';
33
4−import { renderRoute } from '../lib/router'
5−import { isLast } from '../lib/lib'
4+import { renderRoute } from '../lib/router';
5+import { isLast } from '../lib/lib';
66
7−const Crumbs = (props) => {
8− const { routeCrumbs, skipRoute } = props
9− const history = routeCrumbs.reverse()
7+const Crumbs = props => {
8+ const { routeCrumbs, skipRoute } = props;
9+ const history = routeCrumbs.reverse();
1010
1111 // FIXME probably more straightforward to render them normally and just
1212 // return the reversed array of renders
1313 const rendered = history.map((route, idx) => {
14− return (
15− <div className={'flex items-center'} key={ `history-${idx}` }>
16− <Button
17− prop-type={'link'}
18− prop-size={'sm'}
19− key={ `history-button-${idx}` }
20− onClick={ () => skipRoute(history.size - idx - 1) }>
21− { renderRoute(props, route) }
22− </Button>
23−
24− {
25− isLast(history.size, idx)
26− ? <div />
27− : <Chevron className={'h-4 mh-2'} />
28− }
29− </div>
30− )
31− })
32−
33− return rendered
34−}
35−
36−const Header = (props) => {
14+ return (
15+ <div className={'flex items-center'} key={`history-${idx}`}>
16+ <Button
17+ prop-type={'link'}
18+ prop-size={'sm'}
19+ key={`history-button-${idx}`}
20+ onClick={() => skipRoute(history.size - idx - 1)}>
21+ {renderRoute(props, route)}
22+ </Button>
23+
24+ {isLast(history.size, idx) ? (
25+ <div />
26+ ) : (
27+ <Chevron className={'h-4 mh-2'} />
28+ )}
29+ </div>
30+ );
31+ });
32+
33+ return rendered;
34+};
35+
36+const Header = props => {
3737 const crumbs = (
3838 <div className={'flex items-center h-10'}>
3939 <Crumbs
40− routeCrumbs={ props.routeCrumbs }
41− skipRoute={ props.skipRoute }
42− networkType={ props.networkType }
43− wallet={ props.wallet }
44− pointCursor={ props.pointCursor }
40+ routeCrumbs={props.routeCrumbs}
41+ skipRoute={props.skipRoute}
42+ networkType={props.networkType}
43+ wallet={props.wallet}
44+ pointCursor={props.pointCursor}
4545 />
4646 </div>
47− )
47+ );
4848
49− const noCrumbs = <h2 className="mt-9 clickable" onClick={() => props.skipRoute(1)}>Bridge</h2>
49+ const noCrumbs = (
50+ <h2 className="mt-9 clickable" onClick={() => props.skipRoute(1)}>
51+ Bridge
52+ </h2>
53+ );
5054
51− return props.showCrumbs ? crumbs : noCrumbs
52−}
55+ return props.showCrumbs ? crumbs : noCrumbs;
56+};
5357
54−export default Header
58+export default Header;
src/bridge/components/HorizontalSelector.js+11−17
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
22import PropTypes from 'prop-types';
33
44class HorizontalSelector extends React.Component {
@@ -6,7 +6,7 @@ class HorizontalSelector extends React.Component {
66 super(props);
77
88 this.state = {
9− option: this.props.options[0].value
9+ option: this.props.options[0].value,
1010 };
1111
1212 this.selectOption = this.selectOption.bind(this);
@@ -14,44 +14,38 @@ class HorizontalSelector extends React.Component {
1414
1515 selectOption(opt) {
1616 this.props.onChange(opt);
17− this.setState({option: opt});
17+ this.setState({ option: opt });
1818 }
1919
2020 getOptionElems() {
2121 return this.props.options.map(opt => {
22− let active = (opt.value === this.state.option)
23− ? 'btn-primary active'
24− : '';
22+ let active = opt.value === this.state.option ? 'btn-primary active' : '';
2523 return (
2624 <button
2725 className={`btn ${active} h-10 ph-4 flex sans ${this.props.className}`}
2826 value={opt.value}
2927 onClick={() => this.selectOption(opt.value)}>
30− { opt.title }
28+ {opt.title}
3129 </button>
32− )
33− })
30+ );
31+ });
3432 }
3533
3634 render() {
3735 const optionElems = this.getOptionElems();
3836
39− return (
40− <div className='btn-group'>
41− { optionElems }
42− </div>
43− )
37+ return <div className="btn-group">{optionElems}</div>;
4438 }
4539}
4640
4741HorizontalSelector.propTypes = {
4842 className: PropTypes.string,
49− options: PropTypes.array
43+ options: PropTypes.array,
5044};
5145
5246HorizontalSelector.defaultProps = {
5347 className: '',
54− options: []
48+ options: [],
5549};
5650
57−export default HorizontalSelector
51+export default HorizontalSelector;
src/bridge/components/InnerLabel.js+5−6
@@ -1,23 +1,22 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
54const InnerLabel = ({ children, className, style }) => {
65 return (
7− <div className={`abs tl-0 pt-2 pl-4 fs-3 text-500 sans ${className}`} style={style}>
8− { children }
6+ <div
7+ className={`abs tl-0 pt-2 pl-4 fs-3 text-500 sans ${className}`}
8+ style={style}>
9+ {children}
910 </div>
10− )
11+ );
1112};
1213
13−
1414InnerLabel.propTypes = {
1515 className: PropTypes.string,
1616 children: PropTypes.node,
1717 style: PropTypes.object,
1818};
1919
20−
2120InnerLabel.defaultProps = {
2221 className: '',
2322 style: {},
src/bridge/components/InnerLabelDropdown.js+36−35
@@ -1,65 +1,63 @@
1−import React from 'react'
1+import React from 'react';
22import PropTypes from 'prop-types';
33
4−import { DropdownItem, DropdownDivider } from '../components/Base'
4+import { DropdownItem, DropdownDivider } from '../components/Base';
55
66class InnerLabelDropdown extends React.Component {
77 constructor(props) {
8− super(props)
8+ super(props);
99
1010 this.state = {
11− isOpen: false
12− }
11+ isOpen: false,
12+ };
1313
14− this.toggle = this.toggle.bind(this)
15− this.close = this.close.bind(this)
14+ this.toggle = this.toggle.bind(this);
15+ this.close = this.close.bind(this);
1616 }
1717
1818 toggle() {
1919 this.setState({
20− isOpen: !this.state.isOpen
21− })
20+ isOpen: !this.state.isOpen,
21+ });
2222 }
2323
2424 close() {
2525 this.setState({
26− isOpen: false
27− })
26+ isOpen: false,
27+ });
2828 }
2929
3030 selectOption(opt) {
31− this.close()
32− this.props.handleUpdate(opt)
31+ this.close();
32+ this.props.handleUpdate(opt);
3333 }
3434
3535 getOptionElems() {
3636 return this.props.options.map((opt, i) => {
37− if (opt.type === "divider") {
38− return <DropdownDivider key={`divider-${i}`} />
37+ if (opt.type === 'divider') {
38+ return <DropdownDivider key={`divider-${i}`} />;
3939 } else {
4040 return (
41− <DropdownItem key={opt.title} onClick={() => this.selectOption(opt.value)}>
42− { opt.title }
41+ <DropdownItem
42+ key={opt.title}
43+ onClick={() => this.selectOption(opt.value)}>
44+ {opt.title}
4345 </DropdownItem>
44− )
46+ );
4547 }
46− })
48+ });
4749 }
4850
4951 render() {
50− const { props, state } = this
52+ const { props, state } = this;
5153
52− const optionElems = this.getOptionElems()
54+ const optionElems = this.getOptionElems();
5355
54− const menuVisibility = state.isOpen === true
55− ? 'visible'
56− : 'hidden';
56+ const menuVisibility = state.isOpen === true ? 'visible' : 'hidden';
5757
58− const selectedClasses = state.isOpen === true
59− ? 'br-blue'
60− : 'br-gray-50';
58+ const selectedClasses = state.isOpen === true ? 'br-blue' : 'br-gray-50';
6159
62− const fullWidthClass = props.fullWidth ? 'full-width' : ''
60+ const fullWidthClass = props.fullWidth ? 'full-width' : '';
6361
6462 return (
6563 <div className={`rel table ${fullWidthClass} ${props.className}`}>
@@ -73,24 +71,27 @@ class InnerLabelDropdown extends React.Component {
7371 onClick={this.toggle}
7472 disabled={props.disabled}>
7573 <div className={'flex'}>
76− <div className={`ph-4 flex align-center b-2 br-0 b-solid h-10 ${selectedClasses} ${fullWidthClass}`}>
74+ <div
75+ className={`ph-4 flex align-center b-2 br-0 b-solid h-10 ${selectedClasses} ${fullWidthClass}`}>
7776 <div className={'text-400 mr-2'}>{props.title}</div>
7877 <div className={'text-600'}>{props.currentSelectionTitle}</div>
7978 </div>
80− <div className={'white bg-blue s-10 flex-center-all text-sm'}>{'▼'}</div>
79+ <div className={'white bg-blue s-10 flex-center-all text-sm'}>
80+ {'▼'}
81+ </div>
8182 </div>
8283 </button>
8384
84− <div className={`abs flex-column bg-white t-12 l-0 w-100 z-1 ${menuVisibility}`}>
85+ <div
86+ className={`abs flex-column bg-white t-12 l-0 w-100 z-1 ${menuVisibility}`}>
8587 <div className={'pv-1 b-solid b-gray-50 b-2 bb-0'} />
8688
87− { optionElems }
89+ {optionElems}
8890
8991 <div className={'pv-1 b-solid b-gray-50 b-2 bt-0'} />
9092 </div>
91−
9293 </div>
93− )
94+ );
9495 }
9596}
9697
@@ -111,4 +112,4 @@ InnerLabelDropdown.defaultProps = {
111112 currentSelectionTitle: '',
112113};
113114
114−export default InnerLabelDropdown
115+export default InnerLabelDropdown;
src/bridge/components/Input.js+30−36
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
33import ProtoInput from './ProtoInput';
44
55const Input = props => {
6−
76 const _disabled = props.disabled === true ? 'br-gray-30' : '';
87
98 // This enables all children of this component to share the state of this
@@ -15,14 +14,13 @@ const Input = props => {
1514 focussed: props.focussed,
1615 beenFocussed: props.beenFocussed,
1716 error: props.error,
18− }
19− return React.cloneElement(child, propsToPass)
17+ };
18+ return React.cloneElement(child, propsToPass);
2019 });
2120
2221 const className = props.advanced
2322 ? `${props.valClassname} ${props.className}`
24− : `br-gray-50 f-br-blue outline-blue ${props.className}`
25−
23+ : `br-gray-50 f-br-blue outline-blue ${props.className}`;
2624
2725 return (
2826 <div className={'relative'}>
@@ -30,44 +28,40 @@ const Input = props => {
3028 {...props}
3129 // prop-focussed={props.focussed.toString()}
3230 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
33− onChange={e => props.onChange(e.target.value)} />
34− {
35− props.displayFailure === true && props.advanced === true
36− ? <div className={'yellow text-600 h-6 mt-1 fs-3'}>{props.error}</div>
37− : <div/>
38− }
39− {
40− childrenWithProps
41− }
31+ onChange={e => props.onChange(e.target.value)}
32+ />
33+ {props.displayFailure === true && props.advanced === true ? (
34+ <div className={'yellow text-600 h-6 mt-1 fs-3'}>{props.error}</div>
35+ ) : (
36+ <div />
37+ )}
38+ {childrenWithProps}
4239 </div>
4340 );
44−}
45−
41+};
4642
4743Input.propTypes = {
48− focussed: PropTypes.bool,
49− beenFocussed: PropTypes.bool,
50− hasSubmitted: PropTypes.bool,
51− displayPass: PropTypes.bool,
52− displayFailure: PropTypes.bool,
53− advanced: PropTypes.bool,
54− error: PropTypes.string,
55− valClassname: PropTypes.string,
56− size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
44+ focussed: PropTypes.bool,
45+ beenFocussed: PropTypes.bool,
46+ hasSubmitted: PropTypes.bool,
47+ displayPass: PropTypes.bool,
48+ displayFailure: PropTypes.bool,
49+ advanced: PropTypes.bool,
50+ error: PropTypes.string,
51+ valClassname: PropTypes.string,
52+ size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
5753};
5854
59−
6055Input.defaultProps = {
61− focussed: false,
62− beenFocussed: false,
63− hasSubmitted: false,
64− displayPass: false,
65− displayFailure: false,
66− advanced: false,
67− error: '',
68− size: 'm',
69− format: 'normal',
56+ focussed: false,
57+ beenFocussed: false,
58+ hasSubmitted: false,
59+ displayPass: false,
60+ displayFailure: false,
61+ advanced: false,
62+ error: '',
63+ size: 'm',
64+ format: 'normal',
7065};
7166
72−
7367export default Input;
src/bridge/components/InputCaption.js+5−6
@@ -1,23 +1,22 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
54const InputCaption = ({ children, className, style }) => {
65 return (
7− <div className={`mt-8 mb-1 text-35 text-400 sans ${className}`} style={style}>
8− { children }
6+ <div
7+ className={`mt-8 mb-1 text-35 text-400 sans ${className}`}
8+ style={style}>
9+ {children}
910 </div>
10− )
11+ );
1112};
1213
13−
1414InputCaption.propTypes = {
1515 className: PropTypes.string,
1616 children: PropTypes.node,
1717 style: PropTypes.object,
1818};
1919
20−
2120InputCaption.defaultProps = {
2221 className: '',
2322 style: {},
src/bridge/components/Label.js+2−5
@@ -1,23 +1,20 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−
54const Label = ({ children, className }) => {
65 return (
76 <div className={`mb-1 mt-8 fs-4 text-600 sans ${className}`}>
8− { children }
7+ {children}
98 </div>
10− )
9+ );
1110};
1211
13−
1412Label.propTypes = {
1513 className: PropTypes.string,
1614 children: PropTypes.node,
1715 style: PropTypes.object,
1816};
1917
20−
2118Label.defaultProps = {
2219 className: '',
2320 style: {},
src/bridge/components/OldHeader.js+38−41
@@ -1,52 +1,49 @@
1−import React from 'react'
2−import { Button, Chevron } from '../components/Base'
1+import React from 'react';
2+import { Button, Chevron } from '../components/Base';
33
4−import { renderRoute } from '../lib/router'
5−import { isLast } from '../lib/lib'
4+import { renderRoute } from '../lib/router';
5+import { isLast } from '../lib/lib';
66
7−const Crumbs = (props) => {
8− const { routeCrumbs, skipRoute } = props
9− const history = routeCrumbs.reverse()
7+const Crumbs = props => {
8+ const { routeCrumbs, skipRoute } = props;
9+ const history = routeCrumbs.reverse();
1010
1111 // FIXME probably more straightforward to render them normally and just
1212 // return the reversed array of renders
1313 const rendered = history.map((route, idx) => {
14−
15− return (
16− <div className={'flex items-center'} key={ `history-${idx}` }>
17−
18− <Button
19− size={'s'}
20− type={'link'}
21− color={'black'}
22− key={ `history-button-${idx}` }
23− onClick={ () => skipRoute(history.size - idx - 1) }
24− >
25− { `${renderRoute(props, route)}` }
26− </Button>
27−
28− {
29− isLast(history.size, idx)
30− ? <div />
31− : <Chevron className={'h-4 mh-2'} />
32− }
33−
34− </div>
35− )
36− })
37−
38− return rendered
39−}
40−
41−const Header = (props) =>
14+ return (
15+ <div className={'flex items-center'} key={`history-${idx}`}>
16+ <Button
17+ size={'s'}
18+ type={'link'}
19+ color={'black'}
20+ key={`history-button-${idx}`}
21+ onClick={() => skipRoute(history.size - idx - 1)}>
22+ {`${renderRoute(props, route)}`}
23+ </Button>
24+
25+ {isLast(history.size, idx) ? (
26+ <div />
27+ ) : (
28+ <Chevron className={'h-4 mh-2'} />
29+ )}
30+ </div>
31+ );
32+ });
33+
34+ return rendered;
35+};
36+
37+const Header = props => (
4238 <div className={'flex items-center h-8 pt-4'}>
4339 <Crumbs
44− routeCrumbs={ props.routeCrumbs }
45− skipRoute={ props.skipRoute }
46− networkType={ props.networkType }
47− wallet={ props.wallet }
48− pointCursor={ props.pointCursor }
40+ routeCrumbs={props.routeCrumbs}
41+ skipRoute={props.skipRoute}
42+ networkType={props.networkType}
43+ wallet={props.wallet}
44+ pointCursor={props.pointCursor}
4945 />
5046 </div>
47+);
5148
52−export default Header
49+export default Header;
src/bridge/components/OldLink.js+9−16
@@ -14,12 +14,9 @@ const Link = ({
1414 color,
1515 type,
1616}) => {
17−
18−
19−
20− const _color = disabled ? 'disabled' : color
21− const _s = getStyles(styleManifest, type, _color, size)
22− const _disabled = disabled ? 'disabled': ''
17+ const _color = disabled ? 'disabled' : color;
18+ const _s = getStyles(styleManifest, type, _color, size);
19+ const _disabled = disabled ? 'disabled' : '';
2320
2421 return (
2522 <a
@@ -29,12 +26,11 @@ const Link = ({
2926 disabled={disabled}
3027 autoFocus={autoFocus}
3128 href={href}
32− target={target}
33− >
34− { children }
29+ target={target}>
30+ {children}
3531 </a>
36− )
37−}
32+ );
33+};
3834
3935const getStyles = (m, t, c, s) => `${m[t][c]} ${m[t][s]} ${m[t]['all']}`;
4036
@@ -98,9 +94,8 @@ const styleManifest = {
9894 l: 'h-13 ph-6 fs-5',
9995 // Applied to all block buttons
10096 all: 'white b-2 br-solid',
101− }
102−}
103−
97+ },
98+};
10499
105100Link.propTypes = {
106101 disabled: PropTypes.bool,
@@ -111,7 +106,6 @@ Link.propTypes = {
111106 style: PropTypes.object,
112107};
113108
114−
115109Link.defaultProps = {
116110 className: '',
117111 autoFocus: false,
@@ -123,5 +117,4 @@ Link.defaultProps = {
123117 style: {},
124118};
125119
126−
127120export default Link;
src/bridge/components/OldTextArea.js+8−15
@@ -30,7 +30,6 @@ const TextArea = ({
3030 displayFailure,
3131 error,
3232}) => {
33−
3433 const _disabled = disabled === true ? 'br-gray-30' : '';
3534
3635 const s = getStyles(styleManifest, format, size);
@@ -58,15 +57,12 @@ const TextArea = ({
5857 maxLength={maxLength}
5958 readOnly={readOnly}
6059 />
61− {
62− childrenWithProps
63− }
64− {
65− displayFailure === true && isValidated === true
66− ? <div className={'yellow text-600 h-6 mt-1'}>{error}</div>
67− : <div/>
68− }
69−
60+ {childrenWithProps}
61+ {displayFailure === true && isValidated === true ? (
62+ <div className={'yellow text-600 h-6 mt-1'}>{error}</div>
63+ ) : (
64+ <div />
65+ )}
7066 </div>
7167 );
7268};
@@ -78,9 +74,8 @@ const styleManifest = {
7874 l: 'h-80 fs-7 ph-4',
7975 m: 'h-40 fs-35 ph-4',
8076 s: 'h-24 fs-35 ph-1',
81− }
82−}
83−
77+ },
78+};
8479
8580TextArea.propTypes = {
8681 disabled: PropTypes.bool,
@@ -105,7 +100,6 @@ TextArea.propTypes = {
105100 validationClassnames: PropTypes.string,
106101};
107102
108−
109103TextArea.defaultProps = {
110104 disabled: false,
111105 onChange: () => {},
@@ -125,5 +119,4 @@ TextArea.defaultProps = {
125119 validationClassnames: 'br-gray-50 f-br-blue outline-blue',
126120};
127121
128−
129122export default TextArea;
src/bridge/components/P.js+6−9
@@ -1,16 +1,15 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const P = ({ className, children, style, measure }) => {
5−
6− const s = getStyles(styleManifest, measure)
5+ const s = getStyles(styleManifest, measure);
76
87 return (
98 <p className={`mb-7 ${s} ${className}`} style={style}>
10− { children }
9+ {children}
1110 </p>
12− )
13−}
11+ );
12+};
1413
1514const getStyles = (sm, m) => sm[m];
1615
@@ -18,7 +17,7 @@ const styleManifest = {
1817 sm: 'measure-sm',
1918 md: 'measure-md',
2019 lg: 'measure-lg',
21−}
20+};
2221
2322P.propTypes = {
2423 className: PropTypes.string,
@@ -26,12 +25,10 @@ P.propTypes = {
2625 style: PropTypes.object,
2726};
2827
29−
3028P.defaultProps = {
3129 className: '',
3230 style: {},
3331 measure: 'md',
3432};
3533
36−
3734export default P;
src/bridge/components/Passport.js+125−129
@@ -1,240 +1,236 @@
1−import React from 'react'
2−import { pour } from 'sigil-js'
3−import * as ob from 'urbit-ob'
4−import ReactSVGComponents from './ReactSVGComponents'
5−import PropTypes from 'prop-types'
6−import PaperCollateralRenderer from 'PaperCollateralRenderer'
7−import { Button } from './Base'
1+import React from 'react';
2+import { pour } from 'sigil-js';
3+import * as ob from 'urbit-ob';
4+import ReactSVGComponents from './ReactSVGComponents';
5+import PropTypes from 'prop-types';
6+import PaperCollateralRenderer from 'PaperCollateralRenderer';
7+import { Button } from './Base';
88
9−import { WALLET_STATES } from '../lib/invite'
10−import { downloadWallet } from '../lib/invite'
9+import { WALLET_STATES } from '../lib/invite';
10+import { downloadWallet } from '../lib/invite';
1111
1212class Passport extends React.Component {
13−
1413 constructor(props) {
15− super(props)
14+ super(props);
1615
1716 this.state = {
18− paper: null
19− }
17+ paper: null,
18+ };
2019
21− this.masterCensor = this.generateCensor()
22− this.managementCensor = this.generateCensor()
20+ this.masterCensor = this.generateCensor();
21+ this.managementCensor = this.generateCensor();
2322
24− this.download = this.download.bind(this)
23+ this.download = this.download.bind(this);
2524 }
2625
2726 download() {
28− downloadWallet(this.state.paper)
29− this.props.pushWalletState(WALLET_STATES.DOWNLOADED)
27+ downloadWallet(this.state.paper);
28+ this.props.pushWalletState(WALLET_STATES.DOWNLOADED);
3029 }
3130
3231 generateCensor() {
3332 let blocks = [];
3433
35− (new Uint32Array(86)).forEach((_, i) => {
36− let rand = Math.round(Math.random())
37− let filled = (rand || i === 0 || i === 43)
34+ new Uint32Array(86).forEach((_, i) => {
35+ let rand = Math.round(Math.random());
36+ let filled = rand || i === 0 || i === 43;
3837
39− let className = filled ? "passport-block bg-gray-30" : "passport-block"
38+ let className = filled ? 'passport-block bg-gray-30' : 'passport-block';
4039
41− blocks.push(<div key={i} data-key={i}className={className}></div>)
42− })
40+ blocks.push(<div key={i} data-key={i} className={className}></div>);
41+ });
4342
44− return (
45− <div className="flex flex-wrap passport-censor">
46− {blocks}
47− </div>
48− )
43+ return <div className="flex flex-wrap passport-censor">{blocks}</div>;
4944 }
5045
5146 getCurrentDate() {
52− let date = new Date()
53− const fil = n => n >= 10 ? n : `0${n}`
54− let fmtDate = `${date.getUTCFullYear()}.` +
55−   `${(date.getUTCMonth() + 1)}.` +
56−   `${fil(date.getUTCDate())}`
57−
58− return fmtDate
47+ let date = new Date();
48+ const fil = n => (n >= 10 ? n : `0${n}`);
49+ let fmtDate =
50+ `${date.getUTCFullYear()}.` +
51+ `${date.getUTCMonth() + 1}.` +
52+ `${fil(date.getUTCDate())}`;
53+
54+ return fmtDate;
5955 }
6056
6157 getWalletProgress() {
62− let { walletStates } = this.props
58+ let { walletStates } = this.props;
6359
64− if ( !walletStates.includes(WALLET_STATES.UNLOCKING) ) return null
65− if ( walletStates.includes(WALLET_STATES.TRANSACTIONS) ) return null
60+ if (!walletStates.includes(WALLET_STATES.UNLOCKING)) return null;
61+ if (walletStates.includes(WALLET_STATES.TRANSACTIONS)) return null;
6662
6763 let progressTimes = {
68− [WALLET_STATES.UNLOCKING]: {ms: "2813", pct: "44%"},
69− [WALLET_STATES.GENERATING]: {ms: "2433", pct: "38%"},
70− [WALLET_STATES.RENDERING]: {ms: "986", pct: "15%"}
71− }
64+ [WALLET_STATES.UNLOCKING]: { ms: '2813', pct: '44%' },
65+ [WALLET_STATES.GENERATING]: { ms: '2433', pct: '38%' },
66+ [WALLET_STATES.RENDERING]: { ms: '986', pct: '15%' },
67+ };
7268
7369 const progressBars = Object.keys(progressTimes).map(state => {
74− let progressIndex = walletStates.indexOf(state)
75− let progressDone = progressIndex !== -1 && progressIndex < walletStates.length -1
70+ let progressIndex = walletStates.indexOf(state);
71+ let progressDone =
72+ progressIndex !== -1 && progressIndex < walletStates.length - 1;
7673
7774 let progressStyle = {
78− width: progressTimes[state].pct
79− }
75+ width: progressTimes[state].pct,
76+ };
8077
8178 return (
8279 <div className="passport-wallet-progress" style={progressStyle}>
83− {progressDone &&
80+ {progressDone && (
8481 <div className="passport-wallet-progress-fill"></div>
85− }
82+ )}
8683 </div>
87− )
88− })
84+ );
85+ });
8986
90− return (
91− <div className="flex space-between mt-3">
92− {progressBars}
93− </div>
94− )
87+ return <div className="flex space-between mt-3">{progressBars}</div>;
9588 }
9689
9790 getWalletStates() {
98− let { walletStates } = this.props
91+ let { walletStates } = this.props;
9992
100− if ( walletStates.includes(WALLET_STATES.TRANSACTIONS) ) return null
93+ if (walletStates.includes(WALLET_STATES.TRANSACTIONS)) return null;
10194
10295 let stateElems = walletStates.map(state => {
103− if (state === WALLET_STATES.DOWNLOADED) return null
96+ if (state === WALLET_STATES.DOWNLOADED) return null;
10497
105− const lastIndex = walletStates.indexOf(state) === walletStates.length - 1
106− const checkmark = lastIndex
107− ? null
108− : <div className="text-700 text-sm lh-6 green-dark">✓</div>
98+ const lastIndex = walletStates.indexOf(state) === walletStates.length - 1;
99+ const checkmark = lastIndex ? null : (
100+ <div className="text-700 text-sm lh-6 green-dark">✓</div>
101+ );
109102
110103 return (
111104 <div className="flex justify-between">
112105 <div className="text-mono text-sm lh-6 green-dark">{state}</div>
113− { checkmark }
106+ {checkmark}
114107 </div>
115− )
116− })
108+ );
109+ });
117110
118− return (
119− <div className="mt-3">
120− {stateElems}
121− </div>
122− )
111+ return <div className="mt-3">{stateElems}</div>;
123112 }
124113
125114 render() {
126115 let pointVal = this.props.point.matchWith({
127116 Just: p => p.value,
128− Nothing: () => ''
129− })
117+ Nothing: () => '',
118+ });
130119
131− let patp = pointVal === '' ? '' : ob.patp(pointVal)
120+ let patp = pointVal === '' ? '' : ob.patp(pointVal);
132121
133122 let wallet = this.props.wallet.matchWith({
134123 Just: w => w.value,
135− Nothing: () => null
136− })
124+ Nothing: () => null,
125+ });
137126
138127 let ownershipAddress = this.props.wallet.matchWith({
139128 Just: w => w.value.ownership.keys.address,
140− Nothing: () => null
141− })
129+ Nothing: () => null,
130+ });
142131
143− const sigil = patp !== ''
144− ? pour({
132+ const sigil =
133+ patp !== '' ? (
134+ pour({
145135 patp: patp,
146136 renderer: ReactSVGComponents,
147− size: 128
137+ size: 128,
148138 })
149− : <div className="passport-sigil-blank"></div>
150−
151− let currentDate = this.getCurrentDate()
152− let walletStates = this.getWalletStates()
153− let walletProgress = this.getWalletProgress()
154− let paperReady = this.state.paper && this.props.walletStates.includes(WALLET_STATES.PAPER_READY)
155− let downloadColor = paperReady ? 'green' : 'black'
139+ ) : (
140+ <div className="passport-sigil-blank"></div>
141+ );
142+
143+ let currentDate = this.getCurrentDate();
144+ let walletStates = this.getWalletStates();
145+ let walletProgress = this.getWalletProgress();
146+ let paperReady =
147+ this.state.paper &&
148+ this.props.walletStates.includes(WALLET_STATES.PAPER_READY);
149+ let downloadColor = paperReady ? 'green' : 'black';
156150
157151 let downloadBtn = (
158152 <Button
159− disabled={ !paperReady }
160− prop-size={ 'lg wide' }
161− prop-color={ downloadColor }
162− onClick={ this.download }
163− >
164− { 'Download' }
153+ disabled={!paperReady}
154+ prop-size={'lg wide'}
155+ prop-color={downloadColor}
156+ onClick={this.download}>
157+ {'Download'}
165158 </Button>
166− )
167−
168− let paperCollateral = wallet
169− ? (
170− <PaperCollateralRenderer
171− wallet={{[pointVal]: wallet}}
172− className={'extremely-hidden'}
173− callback={data => {
174− this.setState({paper: data})
175− this.props.pushWalletState(WALLET_STATES.PAPER_READY)
176− }}
177− mode={'REGISTRATION'} />
178− )
179− : <div />
159+ );
160+
161+ let paperCollateral = wallet ? (
162+ <PaperCollateralRenderer
163+ wallet={{ [pointVal]: wallet }}
164+ className={'extremely-hidden'}
165+ callback={data => {
166+ this.setState({ paper: data });
167+ this.props.pushWalletState(WALLET_STATES.PAPER_READY);
168+ }}
169+ mode={'REGISTRATION'}
170+ />
171+ ) : (
172+ <div />
173+ );
180174
181175 let walletAddresses = (
182176 <div className="mt-8">
183177 <div className="passport-label text-mono">Ethereum Address</div>
184− <div className="passport-value">{ownershipAddress || 'Ungenerated'}</div>
178+ <div className="passport-value">
179+ {ownershipAddress || 'Ungenerated'}
180+ </div>
185181
186182 <div className="passport-label gray-50 mt-3">Master Ticket</div>
187− <div className="passport-value passport-censor overflow-hidden">{this.masterCensor}</div>
183+ <div className="passport-value passport-censor overflow-hidden">
184+ {this.masterCensor}
185+ </div>
188186
189187 <div className="passport-label gray-50 mt-5">Management Seed</div>
190− <div className="passport-value passport-censor overflow-hidden">{this.managementCensor}</div>
188+ <div className="passport-value passport-censor overflow-hidden">
189+ {this.managementCensor}
190+ </div>
191191 </div>
192− )
192+ );
193193
194194 let walletMetadata = (
195195 <div className="flex">
196− <div className="passport-sigil">
197− {sigil}
198− </div>
196+ <div className="passport-sigil">{sigil}</div>
199197 <div className="passport-metadata ml-4">
200198 <div className="passport-label text-mono">Point</div>
201199 <div className="text-mono">{patp || '-'}</div>
202200
203201 <div className="passport-label gray-50 mt-6">Created on</div>
204− <div className="passport-value">{patp ? currentDate : '-' }</div>
202+ <div className="passport-value">{patp ? currentDate : '-'}</div>
205203
206204 <div className="passport-label gray-50 mt-2">Invited by</div>
207205 <div className="passport-value">{this.props.invitedBy || '-'}</div>
208206 </div>
209207 </div>
210− )
208+ );
211209
212210 return (
213211 <div>
214212 <div className="passport-border">
215213 <div className={`passport ${paperReady && 'passport-filled'}`}>
216− { walletMetadata}
217− { walletAddresses }
218− { paperCollateral }
219−
214+ {walletMetadata}
215+ {walletAddresses}
216+ {paperCollateral}
220217 </div>
221− { downloadBtn }
218+ {downloadBtn}
222219 </div>
223220
224− { walletProgress }
225− { walletStates }
226−
221+ {walletProgress}
222+ {walletStates}
227223 </div>
228− )
224+ );
229225 }
230226}
231227
232228Passport.propTypes = {
233− wallet: PropTypes.object
229+ wallet: PropTypes.object,
234230};
235231
236232Passport.defaultProps = {
237− wallet: null
233+ wallet: null,
238234};
239235
240−export default Passport
236+export default Passport;
src/bridge/components/PointList.js+24−26
@@ -1,30 +1,28 @@
1−import React from 'react'
2−import * as lodash from 'lodash'
3−import { Row } from './Base'
4−import RenderedPoint from './RenderedPoint'
1+import React from 'react';
2+import * as lodash from 'lodash';
3+import { Row } from './Base';
4+import RenderedPoint from './RenderedPoint';
55
66const PointList = props => {
7− const { setPointCursor, routeHandler, points, loading } = props
8− const chunks = lodash.chunk(points, 3)
7+ const { setPointCursor, routeHandler, points, loading } = props;
8+ const chunks = lodash.chunk(points, 3);
99
10− return (
11− points.length === 0
12− ? <p>{loading ? 'Loading...' : 'No points to display' }</p>
13− : lodash.map(chunks, (chunk, idx) =>
14− <Row key={ `render-row-${idx}` }>
15− {
16− lodash.map(chunk, point =>
17− <RenderedPoint
18− key={ point }
19− setPointCursor={ setPointCursor }
20− routeHandler={ routeHandler }
21− point={ point }
22− />
23− )
24− }
25− </Row>
26− )
27− )
28−}
10+ return points.length === 0 ? (
11+ <p>{loading ? 'Loading...' : 'No points to display'}</p>
12+ ) : (
13+ lodash.map(chunks, (chunk, idx) => (
14+ <Row key={`render-row-${idx}`}>
15+ {lodash.map(chunk, point => (
16+ <RenderedPoint
17+ key={point}
18+ setPointCursor={setPointCursor}
19+ routeHandler={routeHandler}
20+ point={point}
21+ />
22+ ))}
23+ </Row>
24+ ))
25+ );
26+};
2927
30−export default PointList
28+export default PointList;
src/bridge/components/ProtoAnchor.js+15−20
@@ -2,31 +2,26 @@ import React from 'react';
22import PropTypes from 'prop-types';
33
44const ProtoAnchor = props => {
5− return (
6− <a {...props}>
7− { props.children }
8− </a>
9− )
10−}
5+ return <a {...props}>{props.children}</a>;
6+};
117
128ProtoAnchor.propTypes = {
13− className: PropTypes.string,
14− children: PropTypes.node,
15− autoFocus: PropTypes.bool,
16− style: PropTypes.object,
17− href: PropTypes.string,
18− rel: PropTypes.string,
19− target: PropTypes.oneOf(['_self','_blank','_parent','_top']),
9+ className: PropTypes.string,
10+ children: PropTypes.node,
11+ autoFocus: PropTypes.bool,
12+ style: PropTypes.object,
13+ href: PropTypes.string,
14+ rel: PropTypes.string,
15+ target: PropTypes.oneOf(['_self', '_blank', '_parent', '_top']),
2016};
2117
2218ProtoAnchor.defaultProps = {
23− className: '',
24− autoFocus: false,
25− style: {},
26− href: '',
27− rel: '',
28− target: '_self',
19+ className: '',
20+ autoFocus: false,
21+ style: {},
22+ href: '',
23+ rel: '',
24+ target: '_self',
2925};
3026
31−
3227export default ProtoAnchor;
src/bridge/components/ProtoButton.js+16−17
@@ -12,29 +12,28 @@ const ProtoButton = props => {
1212 onClick={props.onClick}
1313 disabled={props.disabled}
1414 autoFocus={props.autoFocus}>
15− { props.children }
15+ {props.children}
1616 </button>
17− )
18−}
17+ );
18+};
1919
2020ProtoButton.propTypes = {
21− disabled: PropTypes.bool,
22− onClick: PropTypes.func,
23− className: PropTypes.string,
24− children: PropTypes.node,
25− autofocus: PropTypes.bool,
26− style: PropTypes.object,
27− type: PropTypes.oneOf(['button', 'submit', 'reset'])
21+ disabled: PropTypes.bool,
22+ onClick: PropTypes.func,
23+ className: PropTypes.string,
24+ children: PropTypes.node,
25+ autofocus: PropTypes.bool,
26+ style: PropTypes.object,
27+ type: PropTypes.oneOf(['button', 'submit', 'reset']),
2828};
2929
3030ProtoButton.defaultProps = {
31− className: '',
32− autoFocus: false,
33− onClick: () => {},
34− disabled: false,
35− style: {},
36− type: 'button',
31+ className: '',
32+ autoFocus: false,
33+ onClick: () => {},
34+ disabled: false,
35+ style: {},
36+ type: 'button',
3737};
3838
39−
4039export default ProtoButton;
src/bridge/components/ProtoInput.js+30−32
@@ -1,7 +1,7 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−import { getDomProps } from '../lib/base'
4+import { getDomProps } from '../lib/base';
55
66const ProtoInput = props => {
77 return (
@@ -26,43 +26,41 @@ const ProtoInput = props => {
2626 autoFocus={props.autoFocus}
2727 readOnly={props.readOnly}
2828 maxLength={props.maxLength}
29− autoComplete={props.autoComplete} />
29+ autoComplete={props.autoComplete}
30+ />
3031 );
31−}
32−
32+};
3333
3434ProtoInput.propTypes = {
35− disabled: PropTypes.bool,
36− onChange: PropTypes.func,
37− onBlur: PropTypes.func,
38− onFocus: PropTypes.func,
39− onKeyPress: PropTypes.func,
40− className: PropTypes.string,
41− style: PropTypes.object,
42− children: PropTypes.node,
43− autoFocus: PropTypes.bool,
44− readOnly: PropTypes.bool,
45− name: PropTypes.string,
46− value: PropTypes.string,
47− autoComplete: PropTypes.string,
48− type: PropTypes.string,
35+ disabled: PropTypes.bool,
36+ onChange: PropTypes.func,
37+ onBlur: PropTypes.func,
38+ onFocus: PropTypes.func,
39+ onKeyPress: PropTypes.func,
40+ className: PropTypes.string,
41+ style: PropTypes.object,
42+ children: PropTypes.node,
43+ autoFocus: PropTypes.bool,
44+ readOnly: PropTypes.bool,
45+ name: PropTypes.string,
46+ value: PropTypes.string,
47+ autoComplete: PropTypes.string,
48+ type: PropTypes.string,
4949};
5050
51−
5251ProtoInput.defaultProps = {
53− disabled: false,
54− autoFocus: false,
55− onChange: () => {},
56− onBlur: () => {},
57− onFocus: () => {},
58− onKeyPress: () => {},
59− style: {},
60− className: '',
61− value: '',
62− name: '',
63− autoComplete: 'off',
64− type: 'input',
52+ disabled: false,
53+ autoFocus: false,
54+ onChange: () => {},
55+ onBlur: () => {},
56+ onFocus: () => {},
57+ onKeyPress: () => {},
58+ style: {},
59+ className: '',
60+ value: '',
61+ name: '',
62+ autoComplete: 'off',
63+ type: 'input',
6564};
6665
67−
6866export default ProtoInput;
src/bridge/components/ProtoTextarea.js+28−30
@@ -1,7 +1,7 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−import { getDomProps } from '../lib/base'
4+import { getDomProps } from '../lib/base';
55
66const ProtoTextarea = props => {
77 return (
@@ -24,41 +24,39 @@ const ProtoTextarea = props => {
2424 autoFocus={props.autoFocus}
2525 readOnly={props.readOnly}
2626 maxLength={props.maxLength}
27− autoComplete={props.autoComplete} />
27+ autoComplete={props.autoComplete}
28+ />
2829 );
29−}
30−
30+};
3131
3232ProtoTextarea.propTypes = {
33− disabled: PropTypes.bool,
34− onChange: PropTypes.func,
35− onBlur: PropTypes.func,
36− onFocus: PropTypes.func,
37− onKeyPress: PropTypes.func,
38− className: PropTypes.string,
39− style: PropTypes.object,
40− children: PropTypes.node,
41− autoFocus: PropTypes.bool,
42− readOnly: PropTypes.bool,
43− name: PropTypes.string,
44− value: PropTypes.string,
45− autoComplete: PropTypes.string,
33+ disabled: PropTypes.bool,
34+ onChange: PropTypes.func,
35+ onBlur: PropTypes.func,
36+ onFocus: PropTypes.func,
37+ onKeyPress: PropTypes.func,
38+ className: PropTypes.string,
39+ style: PropTypes.object,
40+ children: PropTypes.node,
41+ autoFocus: PropTypes.bool,
42+ readOnly: PropTypes.bool,
43+ name: PropTypes.string,
44+ value: PropTypes.string,
45+ autoComplete: PropTypes.string,
4646};
4747
48−
4948ProtoTextarea.defaultProps = {
50− disabled: false,
51− autoFocus: false,
52− onChange: () => {},
53− onBlur: () => {},
54− onFocus: () => {},
55− onKeyPress: () => {},
56− style: {},
57− className: '',
58− value: '',
59− name: '',
60− autoComplete: 'off',
49+ disabled: false,
50+ autoFocus: false,
51+ onChange: () => {},
52+ onBlur: () => {},
53+ onFocus: () => {},
54+ onKeyPress: () => {},
55+ style: {},
56+ className: '',
57+ value: '',
58+ name: '',
59+ autoComplete: 'off',
6160};
6261
63−
6462export default ProtoTextarea;
src/bridge/components/ReactSVGComponents.js+42−22
@@ -1,63 +1,83 @@
1−import React from 'react'
2−import { get, map } from 'lodash'
1+import React from 'react';
2+import { get, map } from 'lodash';
33
44const ReactSVGComponents = {
55 svg: (p, key) => {
66 return (
7− <svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'} key={key}>
8− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
7+ <svg
8+ {...p.attr}
9+ version={'1.1'}
10+ xmlns={'http://www.w3.org/2000/svg'}
11+ key={key}>
12+ {map(get(p, 'children', []), (child, idx) =>
13+ ReactSVGComponents[child.tag](child, idx)
14+ )}
915 </svg>
10− )
16+ );
1117 },
1218 circle: (p, key) => {
1319 return (
1420 <circle {...p.attr} key={key}>
15− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
21+ {map(get(p, 'children', []), (child, idx) =>
22+ ReactSVGComponents[child.tag](child, idx)
23+ )}
1624 </circle>
17− )
25+ );
1826 },
1927 rect: (p, key) => {
2028 return (
2129 <rect {...p.attr} key={key}>
22− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
30+ {map(get(p, 'children', []), (child, idx) =>
31+ ReactSVGComponents[child.tag](child, idx)
32+ )}
2333 </rect>
24− )
34+ );
2535 },
2636 path: (p, key) => {
2737 return (
2838 <path {...p.attr} key={key}>
29− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
39+ {map(get(p, 'children', []), (child, idx) =>
40+ ReactSVGComponents[child.tag](child, idx)
41+ )}
3042 </path>
31− )
43+ );
3244 },
3345 g: (p, key) => {
3446 return (
3547 <g {...p.attr} key={key}>
36− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
48+ {map(get(p, 'children', []), (child, idx) =>
49+ ReactSVGComponents[child.tag](child, idx)
50+ )}
3751 </g>
38− )
52+ );
3953 },
4054 polygon: (p, key) => {
4155 return (
4256 <polygon {...p.attr} key={key}>
43− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
57+ {map(get(p, 'children', []), (child, idx) =>
58+ ReactSVGComponents[child.tag](child, idx)
59+ )}
4460 </polygon>
45− )
61+ );
4662 },
4763 line: (p, key) => {
4864 return (
4965 <line {...p.attr} key={key}>
50− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
66+ {map(get(p, 'children', []), (child, idx) =>
67+ ReactSVGComponents[child.tag](child, idx)
68+ )}
5169 </line>
52− )
70+ );
5371 },
5472 polyline: (p, key) => {
5573 return (
5674 <polyline {...p.attr} key={key}>
57− { map(get(p, 'children', []), (child, idx) => ReactSVGComponents[child.tag](child, idx)) }
75+ {map(get(p, 'children', []), (child, idx) =>
76+ ReactSVGComponents[child.tag](child, idx)
77+ )}
5878 </polyline>
59− )
60− }
61−}
79+ );
80+ },
81+};
6282
63−export default ReactSVGComponents
83+export default ReactSVGComponents;
src/bridge/components/RenderedPoint.js+36−35
@@ -1,56 +1,57 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import { Col } from './Base'
4−import { Button } from './Base'
5−import { pour } from 'sigil-js'
6−import * as ob from 'urbit-ob'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import { Col } from './Base';
4+import { Button } from './Base';
5+import { pour } from 'sigil-js';
6+import * as ob from 'urbit-ob';
77
8−import { ROUTE_NAMES } from '../lib/router'
9−import ReactSVGComponents from './ReactSVGComponents'
8+import { ROUTE_NAMES } from '../lib/router';
9+import ReactSVGComponents from './ReactSVGComponents';
1010
11−const RenderedPoint = (props) => {
12− const { setPointCursor, routeHandler, point } = props
11+const RenderedPoint = props => {
12+ const { setPointCursor, routeHandler, point } = props;
1313
14− const name = ob.patp(point)
14+ const name = ob.patp(point);
1515 const sigil = pour({
1616 patp: name,
1717 renderer: ReactSVGComponents,
18− size: 128
19− })
18+ size: 128,
19+ });
2020
2121 return (
22− <Col className={'col-md-3 mb-8'} key={ `rendered-${point}` }>
23− <div key={ `rendered-sigil-${point}` }
22+ <Col className={'col-md-3 mb-8'} key={`rendered-${point}`}>
23+ <div
24+ key={`rendered-sigil-${point}`}
2425 className="clickable"
25− onClick={ () => {
26− setPointCursor(Maybe.Just(point))
27− routeHandler(ROUTE_NAMES.SHIP)
26+ onClick={() => {
27+ setPointCursor(Maybe.Just(point));
28+ routeHandler(ROUTE_NAMES.SHIP);
2829 }}>
29− { sigil }
30+ {sigil}
3031 </div>
31− <div key={ `rendered-name-${point}` }
32+ <div
33+ key={`rendered-name-${point}`}
3234 className="clickable"
33− onClick={ () => {
34− setPointCursor(Maybe.Just(point))
35− routeHandler(ROUTE_NAMES.SHIP)
35+ onClick={() => {
36+ setPointCursor(Maybe.Just(point));
37+ routeHandler(ROUTE_NAMES.SHIP);
3638 }}>
37− <code>{ name }</code>
39+ <code>{name}</code>
3840 </div>
39− <div key={ `rendered-deets-${point}` }>
41+ <div key={`rendered-deets-${point}`}>
4042 <Button
41− key={ `rendered-deets-button-${point}` }
43+ key={`rendered-deets-button-${point}`}
4244 prop-type={'link'}
4345 prop-size={'sm'}
44− onClick={ () => {
45− setPointCursor(Maybe.Just(point))
46− routeHandler(ROUTE_NAMES.SHIP)
47− }}
48− >
49− { 'Details →' }
46+ onClick={() => {
47+ setPointCursor(Maybe.Just(point));
48+ routeHandler(ROUTE_NAMES.SHIP);
49+ }}>
50+ {'Details →'}
5051 </Button>
5152 </div>
5253 </Col>
53− )
54−}
54+ );
55+};
5556
56−export default RenderedPoint
57+export default RenderedPoint;
src/bridge/components/Row.js+4−9
@@ -1,16 +1,12 @@
11import React from 'react';
22import PropTypes from 'prop-types';
33
4−const Row = ({
5− className,
6− children,
7− style,
8−}) => {
4+const Row = ({ className, children, style }) => {
95 return (
106 <div className={`row ${className}`} style={style}>
11− { children }
7+ {children}
128 </div>
13− )
9+ );
1410};
1511
1612Row.propTypes = {
@@ -24,5 +20,4 @@ Row.defaultProps = {
2420 style: {},
2521};
2622
27−
28−export default Row
23+export default Row;
src/bridge/components/RowCol.js+5−10
@@ -1,18 +1,15 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
4−import { Row, Col } from './Base'
4+import { Row, Col } from './Base';
55
66const RowCol = ({ className, children, style }) => {
77 return (
88 <Row className={`${className}`} style={style}>
9− <Col>
10− { children }
11− </Col>
9+ <Col>{children}</Col>
1210 </Row>
13− )
14−}
15−
11+ );
12+};
1613
1714RowCol.propTypes = {
1815 className: PropTypes.string,
@@ -20,11 +17,9 @@ RowCol.propTypes = {
2017 style: PropTypes.object,
2118};
2219
23−
2420RowCol.defaultProps = {
2521 className: '',
2622 style: {},
2723};
2824
29−
3025export default RowCol;
src/bridge/components/ShowBlockie.js+32−43
@@ -1,81 +1,70 @@
1−import React from 'react'
2−import PropTypes from 'prop-types'
3−import makeBlockie from 'ethereum-blockies-base64'
4−import Button from './Button'
5−import { invert } from '../lib/state'
1+import React from 'react';
2+import PropTypes from 'prop-types';
3+import makeBlockie from 'ethereum-blockies-base64';
4+import Button from './Button';
5+import { invert } from '../lib/state';
66
7−const isValidAddress = (address) => /^0x[0-9a-fA-F]{40}$/.test(address);
7+const isValidAddress = address => /^0x[0-9a-fA-F]{40}$/.test(address);
88
99class ShowBlockie extends React.Component {
10−
1110 constructor(props) {
12− super(props)
11+ super(props);
1312
1413 this.state = {
15− open: false
16− }
14+ open: false,
15+ };
1716
18− this.toggle = this.toggle.bind(this)
17+ this.toggle = this.toggle.bind(this);
1918 // this.close = this.close.bind(this)
20−
2119 }
2220
23− toggle = () => this.setState(invert('open', this.state))
21+ toggle = () => this.setState(invert('open', this.state));
2422
2523 render() {
26− const { props, state } = this
24+ const { props, state } = this;
2725
28− const display = state.open
29− ? ''
30− : 'super-hidden'
26+ const display = state.open ? '' : 'super-hidden';
3127
32− const isValid = isValidAddress(props.address)
28+ const isValid = isValidAddress(props.address);
3329
3430 return (
3531 <div className={`flex flex-column items-start ${props.className}`}>
3632 <Button
3733 onClick={this.toggle}
3834 disabled={!isValid}
39− prop-type='link'
40− prop-size='sm'>
41− { state.open
42− ? 'Hide Blockie'
43− : 'Display Blockie'
44− }
45− </Button>
46− {
47− !isValid
48− ? <div/>
49− : <img
50− className={`${display} mt-2`}
51− alt={ props.address }
52− src={ makeBlockie(props.address) }
53− style={{
54− width: props.size,
55− height: props.size,
56− }} />
57− }
58−
35+ prop-type="link"
36+ prop-size="sm">
37+ {state.open ? 'Hide Blockie' : 'Display Blockie'}
38+ </Button>
39+ {!isValid ? (
40+ <div />
41+ ) : (
42+ <img
43+ className={`${display} mt-2`}
44+ alt={props.address}
45+ src={makeBlockie(props.address)}
46+ style={{
47+ width: props.size,
48+ height: props.size,
49+ }}
50+ />
51+ )}
5952 </div>
60− )
53+ );
6154 }
6255}
6356
64−
6557ShowBlockie.propTypes = {
6658 className: PropTypes.string,
6759 children: PropTypes.node,
6860 style: PropTypes.object,
6961};
7062
71−
7263ShowBlockie.defaultProps = {
7364 className: '',
7465 style: {},
7566 size: 64,
7667 address: '',
77−
7868};
7969
80−
8170export default ShowBlockie;
src/bridge/components/Spacing.js+1−8
@@ -2,17 +2,11 @@ import React from 'react';
22import PropTypes from 'prop-types';
33import { isUndefined } from 'lodash';
44
5−
65const Spacing = ({ className, children, h, w }) => {
7−
86 const h_cn = isUndefined(h) ? '' : `mt-${h}`;
97 const w_cn = isUndefined(w) ? '' : `ml-${w}`;
108
11− return (
12− <div className={`${h_cn} ${w_cn} ${className}`}>
13− { children }
14− </div>
15− );
9+ return <div className={`${h_cn} ${w_cn} ${className}`}>{children}</div>;
1610};
1711
1812Spacing.propTypes = {
@@ -28,5 +22,4 @@ Spacing.defaultProps = {
2822 w: 0,
2923};
3024
31−
3225export default Spacing;
src/bridge/components/StatelessTransaction.js+297−281
@@ -1,17 +1,16 @@
1−import { Nothing, Just } from 'folktale/maybe'
2−import { Ok } from 'folktale/result'
3−import React from 'react'
4−import * as need from '../lib/need'
1+import { Nothing, Just } from 'folktale/maybe';
2+import { Ok } from 'folktale/result';
3+import React from 'react';
4+import * as need from '../lib/need';
55
6−import { Code, H3 } from './Base'
7−import { Button } from './Base'
8−import { CheckboxButton, Input, InnerLabel,
9− InnerLabelDropdown } from './Base'
10−import { Warning } from '../components/Base'
6+import { Code, H3 } from './Base';
7+import { Button } from './Base';
8+import { CheckboxButton, Input, InnerLabel, InnerLabelDropdown } from './Base';
9+import { Warning } from '../components/Base';
1110
12−import { BRIDGE_ERROR } from '../lib/error'
13−import { ROUTE_NAMES } from '../lib/router'
14−import * as tank from '../lib/tank'
11+import { BRIDGE_ERROR } from '../lib/error';
12+import { ROUTE_NAMES } from '../lib/router';
13+import * as tank from '../lib/tank';
1514
1615import {
1716 sendSignedTransaction,
@@ -19,20 +18,19 @@ import {
1918 toWei,
2019 hexify,
2120 renderSignedTx,
22− signTransaction
23− } from '../lib/txn'
21+ signTransaction,
22+} from '../lib/txn';
2423
2524const SUBMISSION_STATES = {
2625 PROMPT: 'Send transaction',
2726 PREPARING: 'Preparing...',
2827 FUNDING: 'Finding transaction funding...',
29− SENDING: 'Sending transaction...'
28+ SENDING: 'Sending transaction...',
3029};
3130
3231class StatelessTransaction extends React.Component {
33−
3432 constructor(props) {
35− super(props)
33+ super(props);
3634
3735 this.state = {
3836 gasPrice: '5',
@@ -45,37 +43,37 @@ class StatelessTransaction extends React.Component {
4543 stx: Nothing(),
4644 txn: Nothing(),
4745 txStatus: SUBMISSION_STATES.PROMPT,
48− txError: Nothing()
49− }
50−
51− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
52− this.setUserApproval = this.setUserApproval.bind(this)
53− this.setTxn = this.setTxn.bind(this)
54− this.setStx = this.setStx.bind(this)
55− this.sendTxn = this.sendTxn.bind(this)
56− this.setNonce = this.setNonce.bind(this)
57− this.setChainId = this.setChainId.bind(this)
58− this.setGasPrice = this.setGasPrice.bind(this)
59− this.setGasLimit = this.setGasLimit.bind(this)
60− this.rangeChange = this.rangeChange.bind(this)
61− this.toggleGasDetails = this.toggleGasDetails.bind(this)
62− this.handleChainUpdate = this.handleChainUpdate.bind(this)
46+ txError: Nothing(),
47+ };
48+
49+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
50+ this.setUserApproval = this.setUserApproval.bind(this);
51+ this.setTxn = this.setTxn.bind(this);
52+ this.setStx = this.setStx.bind(this);
53+ this.sendTxn = this.sendTxn.bind(this);
54+ this.setNonce = this.setNonce.bind(this);
55+ this.setChainId = this.setChainId.bind(this);
56+ this.setGasPrice = this.setGasPrice.bind(this);
57+ this.setGasLimit = this.setGasLimit.bind(this);
58+ this.rangeChange = this.rangeChange.bind(this);
59+ this.toggleGasDetails = this.toggleGasDetails.bind(this);
60+ this.handleChainUpdate = this.handleChainUpdate.bind(this);
6361 }
6462
6563 componentDidMount() {
66− const { props } = this
64+ const { props } = this;
6765
6866 const addr = need.address(props);
6967
7068 props.web3.matchWith({
7169 Nothing: () => {},
72− Just: (w3) => {
73− const validWeb3 = w3.value
70+ Just: w3 => {
71+ const validWeb3 = w3.value;
7472
7573 const getTxMetadata = [
7674 validWeb3.eth.getTransactionCount(addr),
7775 validWeb3.eth.net.getId(),
78− validWeb3.eth.getGasPrice()
76+ validWeb3.eth.getGasPrice(),
7977 ];
8078
8179 Promise.all(getTxMetadata).then(r => {
@@ -85,54 +83,53 @@ class StatelessTransaction extends React.Component {
8583 gasPrice: fromWei(r[2], 'gwei'),
8684 };
8785
88− this.setState({...txMetadata})
89−
90− })
91− }
86+ this.setState({ ...txMetadata });
87+ });
88+ },
9289 });
9390 }
9491
9592 handleChainUpdate(chainId) {
96− if (chainId === "custom") {
93+ if (chainId === 'custom') {
9794 this.setState({
98− customChain: true
99− })
95+ customChain: true,
96+ });
10097 } else {
10198 this.setState({
10299 customChain: false,
103− chainId
104− })
100+ chainId,
101+ });
105102 }
106103
107− this.clearStx()
104+ this.clearStx();
108105 }
109106
110− setUserApproval(){
111− const {state} = this
112− this.setState({ userApproval: !state.userApproval })
107+ setUserApproval() {
108+ const { state } = this;
109+ this.setState({ userApproval: !state.userApproval });
113110 }
114111
115112 toggleGasDetails() {
116113 this.setState({
117− showGasDetails: !this.state.showGasDetails
118− })
114+ showGasDetails: !this.state.showGasDetails,
115+ });
119116 }
120117
121− setStx(stx){
118+ setStx(stx) {
122119 this.setState({
123120 stx,
124121 userApproval: false,
125− })
122+ });
126123 }
127124
128− setTxn(txn){
129− this.setState({ txn })
125+ setTxn(txn) {
126+ this.setState({ txn });
130127 }
131128
132129 createUnsignedTxn() {
133− const txn = this.props.createUnsignedTxn()
130+ const txn = this.props.createUnsignedTxn();
134131
135− this.setState({ txn })
132+ this.setState({ txn });
136133 }
137134
138135 clearTxn() {
@@ -140,38 +137,38 @@ class StatelessTransaction extends React.Component {
140137 userApproval: false,
141138 txn: Nothing(),
142139 stx: Nothing(),
143− })
140+ });
144141 }
145142
146− setNonce(nonce){
147− this.setState({ nonce })
148− this.clearStx()
143+ setNonce(nonce) {
144+ this.setState({ nonce });
145+ this.clearStx();
149146 }
150147
151− setChainId(chainId){
152− this.setState({ chainId })
153− this.clearStx()
148+ setChainId(chainId) {
149+ this.setState({ chainId });
150+ this.clearStx();
154151 }
155152
156153 setGasPrice(gasPrice) {
157− this.setState({ gasPrice })
158− this.clearStx()
154+ this.setState({ gasPrice });
155+ this.clearStx();
159156 }
160157
161158 rangeChange(e) {
162159 this.setGasPrice(e.target.value);
163160 }
164161
165− setGasLimit(gasLimit){
166− this.setState({ gasLimit })
167− this.clearStx()
162+ setGasLimit(gasLimit) {
163+ this.setState({ gasLimit });
164+ this.clearStx();
168165 }
169166
170167 clearStx() {
171168 this.setState({
172169 userApproval: false,
173170 stx: Nothing(),
174− })
171+ });
175172 }
176173
177174 async sendTxn() {
@@ -182,7 +179,7 @@ class StatelessTransaction extends React.Component {
182179 Just: tx => tx.value,
183180 Nothing: () => {
184181 throw BRIDGE_ERROR.MISSING_TXN;
185− }
182+ },
186183 });
187184
188185 if (props.beforeSend) {
@@ -194,7 +191,7 @@ class StatelessTransaction extends React.Component {
194191 console.log('beforeSend error', e);
195192 this.setState({
196193 txStatus: SUBMISSION_STATES.PROMPT,
197− txError: Just('Something went wrong!')
194+ txError: Just('Something went wrong!'),
198195 });
199196 return;
200197 }
@@ -203,11 +200,11 @@ class StatelessTransaction extends React.Component {
203200 this.setState({ txStatus: SUBMISSION_STATES.FUNDING });
204201
205202 const rawTx = hexify(stx.serialize());
206− const cost = (state.gasLimit * toWei(state.gasPrice, 'gwei'));
203+ const cost = state.gasLimit * toWei(state.gasPrice, 'gwei');
207204
208205 const address = need.address(props);
209206 let balance = await web3.eth.getBalance(address);
210− let hasBalance = (balance >= cost);
207+ let hasBalance = balance >= cost;
211208
212209 let usedTank = false;
213210 // if we need to, try and fund the transaction
@@ -222,11 +219,10 @@ class StatelessTransaction extends React.Component {
222219 txStatus: SUBMISSION_STATES.PROMPT,
223220 txError: Just(`Insufficient funds.
224221 Address ${address} needs at least ${fromWei(cost.toString())} ETH,
225− currently has ${fromWei(balance.toString())} ETH.`
226− )
222+ currently has ${fromWei(balance.toString())} ETH.`),
227223 });
228224
229− // if we have the balance, proceed with submission
225+ // if we have the balance, proceed with submission
230226 } else {
231227 this.setState({ txStatus: SUBMISSION_STATES.SENDING });
232228
@@ -236,23 +232,23 @@ class StatelessTransaction extends React.Component {
236232 usedTank,
237233 props.setTxnConfirmations
238234 )
239− .then(txHash => {
240− props.onSent(Just(Ok(txHash)), state.stx.value);
241− props.popRoute();
242−
243− let routeData = {};
244− if (props.networkSeed) {
245− props.setNetworkSeedCache(props.networkSeed);
246− routeData.promptKeyfile = true;
247− }
248− props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, routeData);
249− })
250− .catch(err => {
251− this.setState({
252− txStatus: SUBMISSION_STATES.PROMPT,
253− txError: Just(err)
235+ .then(txHash => {
236+ props.onSent(Just(Ok(txHash)), state.stx.value);
237+ props.popRoute();
238+
239+ let routeData = {};
240+ if (props.networkSeed) {
241+ props.setNetworkSeedCache(props.networkSeed);
242+ routeData.promptKeyfile = true;
243+ }
244+ props.pushRoute(ROUTE_NAMES.SENT_TRANSACTION, routeData);
245+ })
246+ .catch(err => {
247+ this.setState({
248+ txStatus: SUBMISSION_STATES.PROMPT,
249+ txError: Just(err),
250+ });
254251 });
255− });
256252 }
257253 }
258254
@@ -261,101 +257,122 @@ class StatelessTransaction extends React.Component {
261257 // returns true if the gas tank was used, false otherwise
262258 ensureFundsFor(web3, address, cost, signedTxs) {
263259 return new Promise((resolve, reject) => {
264− tank.ensureFundsFor(web3, null, address, cost, signedTxs,
265− () => { resolve(false); })
266− .then(res => { resolve(res); });
260+ tank
261+ .ensureFundsFor(web3, null, address, cost, signedTxs, () => {
262+ resolve(false);
263+ })
264+ .then(res => {
265+ resolve(res);
266+ });
267267 });
268268 }
269269
270270 getChainTitle(chainId) {
271271 let map = {
272− "1": "Mainnet - 1",
273− "2": "Morden - 2",
274− "3": "Ropsten - 3",
275− "4": "Goerli - 4",
276− "42": "Kovan - 42",
277− "1337": "Geth private chains - 1337",
278− "custom": "Custom"
279− }
280−
281− return map[chainId]
272+ '1': 'Mainnet - 1',
273+ '2': 'Morden - 2',
274+ '3': 'Ropsten - 3',
275+ '4': 'Goerli - 4',
276+ '42': 'Kovan - 42',
277+ '1337': 'Geth private chains - 1337',
278+ custom: 'Custom',
279+ };
280+
281+ return map[chainId];
282282 }
283283
284284 getChainOptions() {
285− return [{
286− title: this.getChainTitle("1"),
287− value: "1"
288− }, {
289− title: this.getChainTitle("2"),
290− value: "2"
291− }, {
292− title: this.getChainTitle("3"),
293− value: "3"
294− }, {
295− title: this.getChainTitle("4"),
296− value: "4"
297− }, {
298− title: this.getChainTitle("42"),
299− value: "42"
300− }, {
301− title: this.getChainTitle("1337"),
302− value: "1337"
303− }, {
304− title: this.getChainTitle("custom"),
305− value: "custom"
306− }]
285+ return [
286+ {
287+ title: this.getChainTitle('1'),
288+ value: '1',
289+ },
290+ {
291+ title: this.getChainTitle('2'),
292+ value: '2',
293+ },
294+ {
295+ title: this.getChainTitle('3'),
296+ value: '3',
297+ },
298+ {
299+ title: this.getChainTitle('4'),
300+ value: '4',
301+ },
302+ {
303+ title: this.getChainTitle('42'),
304+ value: '42',
305+ },
306+ {
307+ title: this.getChainTitle('1337'),
308+ value: '1337',
309+ },
310+ {
311+ title: this.getChainTitle('custom'),
312+ value: 'custom',
313+ },
314+ ];
307315 }
308316
309317 render() {
310− const { web3, canGenerate } = this.props
311− const { gasPrice, gasLimit, nonce, chainId,
312− txn, stx, userApproval, showGasDetails,
313− customChain, txStatus } = this.state
318+ const { web3, canGenerate } = this.props;
319+ const {
320+ gasPrice,
321+ gasLimit,
322+ nonce,
323+ chainId,
324+ txn,
325+ stx,
326+ userApproval,
327+ showGasDetails,
328+ customChain,
329+ txStatus,
330+ } = this.state;
314331
315− const { setNonce, setChainId, setGasLimit, setGasPrice, toggleGasDetails,
316− setUserApproval, sendTxn, createUnsignedTxn, handleChainUpdate } = this
332+ const {
333+ setNonce,
334+ setChainId,
335+ setGasLimit,
336+ setGasPrice,
337+ toggleGasDetails,
338+ setUserApproval,
339+ sendTxn,
340+ createUnsignedTxn,
341+ handleChainUpdate,
342+ } = this;
317343
318− const { state } = this
344+ const { state } = this;
319345
320− const canSign = Just.hasInstance(txn)
321− const canApprove = Just.hasInstance(stx)
322− const canSend = Just.hasInstance(stx) && userApproval === true
346+ const canSign = Just.hasInstance(txn);
347+ const canApprove = Just.hasInstance(stx);
348+ const canSend = Just.hasInstance(stx) && userApproval === true;
323349
324− const chainOptions = this.getChainOptions()
350+ const chainOptions = this.getChainOptions();
325351
326− const generateButtonColor =
327− Nothing.hasInstance(txn)
328− ? 'blue'
329− : 'green'
352+ const generateButtonColor = Nothing.hasInstance(txn) ? 'blue' : 'green';
330353
331− const signerButtonColor =
332− Nothing.hasInstance(stx)
333− ? 'blue'
334− : 'green'
354+ const signerButtonColor = Nothing.hasInstance(stx) ? 'blue' : 'green';
335355
336− const generateTxnButton =
356+ const generateTxnButton = (
337357 <Button
338− className={ 'mt-8' }
339− disabled={ !canGenerate }
340− prop-color={ generateButtonColor }
341− prop-size={ 'lg wide' }
342− onClick={ createUnsignedTxn }
343− >
344− { 'Generate Transaction' }
358+ className={'mt-8'}
359+ disabled={!canGenerate}
360+ prop-color={generateButtonColor}
361+ prop-size={'lg wide'}
362+ onClick={createUnsignedTxn}>
363+ {'Generate Transaction'}
345364 </Button>
365+ );
346366
347367 const unsignedTxnDisplay = txn.matchWith({
348368 Nothing: _ => '',
349− Just: tx =>
369+ Just: tx => (
350370 <React.Fragment>
351− <H3 className={ 'mt-8' }>
352− { 'Unsigned Transaction' }
353− </H3>
354− <Code>
355− { JSON.stringify(tx.value, null, 2) }
356− </Code>
371+ <H3 className={'mt-8'}>{'Unsigned Transaction'}</H3>
372+ <Code>{JSON.stringify(tx.value, null, 2)}</Code>
357373 </React.Fragment>
358− })
374+ ),
375+ });
359376
360377 const gasPriceRangeDialogue = (
361378 <React.Fragment>
@@ -366,7 +383,9 @@ class StatelessTransaction extends React.Component {
366383 </div>
367384 <div className="text-sm">
368385 <span>Max transaction fee: </span>
369− <span className="text-700">{(gasPrice * gasLimit) / 1000000000} eth</span>
386+ <span className="text-700">
387+ {(gasPrice * gasLimit) / 1000000000} eth
388+ </span>
370389 </div>
371390 </div>
372391
@@ -378,185 +397,182 @@ class StatelessTransaction extends React.Component {
378397 list="gweiVals"
379398 value={gasPrice}
380399 onChange={this.rangeChange}
381− />
400+ />
382401
383402 <div className="flex space-between text-sm mb-8">
384403 <div>Cheap</div>
385404 <div>Fast</div>
386405 </div>
387406 </React.Fragment>
388− )
407+ );
389408
390409 const toggleGasDetailsDialogue = (
391− <span className="text-link" onClick={toggleGasDetails}>Gas Details</span>
392− )
410+ <span className="text-link" onClick={toggleGasDetails}>
411+ Gas Details
412+ </span>
413+ );
393414
394− const gasPriceDialogue =
415+ const gasPriceDialogue = (
395416 <Input
396− className={ 'mono mt-4' }
397− prop-size={ 'md' }
398− prop-format={ 'innerLabel' }
399− value={ gasPrice }
400− onChange={ setGasPrice }
401− >
402− <InnerLabel>
403− { 'Gas Price (gwei)' }
404− </InnerLabel>
417+ className={'mono mt-4'}
418+ prop-size={'md'}
419+ prop-format={'innerLabel'}
420+ value={gasPrice}
421+ onChange={setGasPrice}>
422+ <InnerLabel>{'Gas Price (gwei)'}</InnerLabel>
405423 </Input>
424+ );
406425
407− const gasLimitDialogue =
426+ const gasLimitDialogue = (
408427 <Input
409− className={ 'mono mt-4' }
410− prop-size={ 'md' }
411− prop-format={ 'innerLabel' }
412− value={ gasLimit }
413− onChange={ setGasLimit }>
414− <InnerLabel>
415− { 'Gas Limit' }
416− </InnerLabel>
428+ className={'mono mt-4'}
429+ prop-size={'md'}
430+ prop-format={'innerLabel'}
431+ value={gasLimit}
432+ onChange={setGasLimit}>
433+ <InnerLabel>{'Gas Limit'}</InnerLabel>
417434 </Input>
435+ );
418436
419− const nonceDialogue =
437+ const nonceDialogue = (
420438 <Input
421− className={ 'mono mt-4 mb-4' }
422− prop-size={ 'md' }
423− prop-format={ 'innerLabel' }
424− value={ nonce }
425− onChange={ setNonce }
426− >
427− <InnerLabel>
428− { 'Nonce' }
429− </InnerLabel>
439+ className={'mono mt-4 mb-4'}
440+ prop-size={'md'}
441+ prop-format={'innerLabel'}
442+ value={nonce}
443+ onChange={setNonce}>
444+ <InnerLabel>{'Nonce'}</InnerLabel>
430445 </Input>
446+ );
431447
432− const chainDialogueTitle = customChain ? "Custom" : this.getChainTitle(chainId)
433− const chainDialogue =
448+ const chainDialogueTitle = customChain
449+ ? 'Custom'
450+ : this.getChainTitle(chainId);
451+ const chainDialogue = (
434452 <InnerLabelDropdown
435453 className={'mt-6'}
436454 fullWidth={true}
437455 title={'Chain ID'}
438456 options={chainOptions}
439457 handleUpdate={handleChainUpdate}
440− currentSelectionTitle={chainDialogueTitle}
441− >
442− </InnerLabelDropdown>
443−
444− const customChainDialogue = !customChain ? null :
445− <Input
446− className={ 'mono mt-4 mb-8' }
447− prop-size={ 'md' }
448− prop-format={ 'innerLabel' }
449− value={ chainId }
450− onChange={ setChainId }
451− >
452− <InnerLabel>
453− { 'Chain ID' }
454− </InnerLabel>
455− </Input>
458+ currentSelectionTitle={chainDialogueTitle}></InnerLabelDropdown>
459+ );
460+
461+ const customChainDialogue = !customChain ? null : (
462+ <Input
463+ className={'mono mt-4 mb-8'}
464+ prop-size={'md'}
465+ prop-format={'innerLabel'}
466+ value={chainId}
467+ onChange={setChainId}>
468+ <InnerLabel>{'Chain ID'}</InnerLabel>
469+ </Input>
470+ );
456471
457472 const onlineParamsDialogue = web3.matchWith({
458473 Just: _ => <div />,
459− Nothing: _ =>
474+ Nothing: _ => (
460475 <React.Fragment>
461− { nonceDialogue }
462− { chainDialogue }
463− { customChainDialogue }
476+ {nonceDialogue}
477+ {chainDialogue}
478+ {customChainDialogue}
464479 </React.Fragment>
465− })
480+ ),
481+ });
466482
467− const signTxnButton =
483+ const signTxnButton = (
468484 <Button
469− disabled={ !canSign }
470− className={ 'mt-8' }
471− prop-size={ 'lg wide' }
472− prop-color={ signerButtonColor }
473− onClick={ () => signTransaction({...this.props, ...this.state, setStx: this.setStx}) }
474− >
475− { 'Sign Transaction' }
485+ disabled={!canSign}
486+ className={'mt-8'}
487+ prop-size={'lg wide'}
488+ prop-color={signerButtonColor}
489+ onClick={() =>
490+ signTransaction({ ...this.props, ...this.state, setStx: this.setStx })
491+ }>
492+ {'Sign Transaction'}
476493 </Button>
494+ );
477495
478496 const signedTxnDisplay = stx.matchWith({
479497 Nothing: _ => '',
480− Just: tx =>
498+ Just: tx => (
481499 <React.Fragment>
482− <H3 className={ 'mt-8' }>
483− { 'Signed Transaction' }
484− </H3>
485− <Code>
486− { JSON.stringify(renderSignedTx(tx.value), null, 2) }
487− </Code>
500+ <H3 className={'mt-8'}>{'Signed Transaction'}</H3>
501+ <Code>{JSON.stringify(renderSignedTx(tx.value), null, 2)}</Code>
488502 </React.Fragment>
489− })
503+ ),
504+ });
490505
491− const confirmButton =
506+ const confirmButton = (
492507 <CheckboxButton
493− className={ 'mt-8' }
494− disabled={ !canApprove }
495− onToggle={ setUserApproval }
496− state={ userApproval }
497− label='I approve this transaction and wish to send.'
498− ></CheckboxButton>
499−
500− const sending = (txStatus !== SUBMISSION_STATES.PROMPT);
508+ className={'mt-8'}
509+ disabled={!canApprove}
510+ onToggle={setUserApproval}
511+ state={userApproval}
512+ label="I approve this transaction and wish to send."></CheckboxButton>
513+ );
514+
515+ const sending = txStatus !== SUBMISSION_STATES.PROMPT;
501516 const sendTxnSpinner = sending ? '' : 'hide';
502− const sendTxnButton =
517+ const sendTxnButton = (
503518 <Button
504− prop-size={ 'xl wide' }
505− className={ 'mt-8' }
506− disabled={ !canSend || sending }
507− onClick={ sendTxn }
508− >
519+ prop-size={'xl wide'}
520+ className={'mt-8'}
521+ disabled={!canSend || sending}
522+ onClick={sendTxn}>
509523 <span className="relative">
510524 <span className={`btn-spinner ${sendTxnSpinner}`}></span>
511− { txStatus }
525+ {txStatus}
512526 </span>
513527 </Button>
528+ );
514529
515530 const sendDialogue = web3.matchWith({
516531 Nothing: _ => '',
517− Just: _ =>
532+ Just: _ => (
518533 <React.Fragment>
519− { confirmButton }
520− { sendTxnButton }
534+ {confirmButton}
535+ {sendTxnButton}
521536 </React.Fragment>
522− })
523−
524− const txnErrorDialogue = Nothing.hasInstance(state.txError)
525− ? ''
526− : <Warning className={'mt-8'}>
527− <H3 style={{marginTop: 0, paddingTop: 0}}>
528− {
529− 'There was an error sending your transaction.'
530− }
531− </H3>
532− { state.txError.value }
537+ ),
538+ });
539+
540+ const txnErrorDialogue = Nothing.hasInstance(state.txError) ? (
541+ ''
542+ ) : (
543+ <Warning className={'mt-8'}>
544+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
545+ {'There was an error sending your transaction.'}
546+ </H3>
547+ {state.txError.value}
533548 </Warning>
549+ );
534550
535551 return (
536552 <React.Fragment>
537− { generateTxnButton }
538− { unsignedTxnDisplay }
553+ {generateTxnButton}
554+ {unsignedTxnDisplay}
539555
540− { gasPriceRangeDialogue }
541− { toggleGasDetailsDialogue }
556+ {gasPriceRangeDialogue}
557+ {toggleGasDetailsDialogue}
542558
543− { showGasDetails &&
559+ {showGasDetails && (
544560 <div>
545− { gasPriceDialogue }
546− { gasLimitDialogue }
561+ {gasPriceDialogue}
562+ {gasLimitDialogue}
547563 </div>
548− }
549− { onlineParamsDialogue }
564+ )}
565+ {onlineParamsDialogue}
550566
551− { signTxnButton }
567+ {signTxnButton}
552568
553− { signedTxnDisplay }
554− { sendDialogue }
569+ {signedTxnDisplay}
570+ {sendDialogue}
555571
556− { txnErrorDialogue }
572+ {txnErrorDialogue}
557573 </React.Fragment>
558− )
574+ );
559575 }
560576}
561577
562−export default StatelessTransaction
578+export default StatelessTransaction;
src/bridge/components/Textarea.js+30−37
@@ -3,7 +3,6 @@ import PropTypes from 'prop-types';
33import ProtoTextarea from './ProtoTextarea';
44
55const Textarea = props => {
6−
76 const _disabled = props.disabled === true ? 'br-gray-30' : '';
87
98 // This enables all children of this component to share the state of this
@@ -15,16 +14,13 @@ const Textarea = props => {
1514 focussed: props.focussed,
1615 beenFocussed: props.beenFocussed,
1716 error: props.error,
18− }
19− return React.cloneElement(child, propsToPass)
17+ };
18+ return React.cloneElement(child, propsToPass);
2019 });
2120
2221 const className = props.advanced
2322 ? `${props.valClassname} ${props.className}`
24− : `br-gray-50 f-br-blue outline-blue ${props.className}`
25−
26−
27−
23+ : `br-gray-50 f-br-blue outline-blue ${props.className}`;
2824
2925 return (
3026 <div className={'relative'}>
@@ -32,43 +28,40 @@ const Textarea = props => {
3228 {...props}
3329 // prop-focussed={props.focussed.toString()}
3430 className={`p-3 b-2 b-solid flex ${_disabled} ${className}`}
35− onChange={e => props.onChange(e.target.value)} />
36− {
37− props.displayFailure === true && props.advanced === true
38− ? <div className={'yellow text-600 h-6 mt-1 fs-3 abs'}>{props.error}</div>
39− : <div/>
40− }
41− {
42− childrenWithProps
43− }
31+ onChange={e => props.onChange(e.target.value)}
32+ />
33+ {props.displayFailure === true && props.advanced === true ? (
34+ <div className={'yellow text-600 h-6 mt-1 fs-3 abs'}>{props.error}</div>
35+ ) : (
36+ <div />
37+ )}
38+ {childrenWithProps}
4439 </div>
4540 );
46−}
41+};
4742
4843Textarea.propTypes = {
49− focussed: PropTypes.bool,
50− beenFocussed: PropTypes.bool,
51− hasSubmitted: PropTypes.bool,
52− displayPass: PropTypes.bool,
53− displayFailure: PropTypes.bool,
54− advanced: PropTypes.bool,
55− error: PropTypes.string,
56− valClassname: PropTypes.string,
57− size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
44+ focussed: PropTypes.bool,
45+ beenFocussed: PropTypes.bool,
46+ hasSubmitted: PropTypes.bool,
47+ displayPass: PropTypes.bool,
48+ displayFailure: PropTypes.bool,
49+ advanced: PropTypes.bool,
50+ error: PropTypes.string,
51+ valClassname: PropTypes.string,
52+ size: PropTypes.oneOf(['s', 'm', 'l', 'xl']),
5853};
5954
60−
6155Textarea.defaultProps = {
62− focussed: false,
63− beenFocussed: false,
64− hasSubmitted: false,
65− displayPass: false,
66− displayFailure: false,
67− advanced: false,
68− error: '',
69− size: 'm',
70− format: 'normal',
56+ focussed: false,
57+ beenFocussed: false,
58+ hasSubmitted: false,
59+ displayPass: false,
60+ displayFailure: false,
61+ advanced: false,
62+ error: '',
63+ size: 'm',
64+ format: 'normal',
7165};
7266
73−
7467export default Textarea;
src/bridge/components/UploadButton.js+12−10
@@ -12,16 +12,18 @@ class UploadButton extends React.Component {
1212
1313 render() {
1414 return (
15− <label className={this.props.className}>
16− <input
17− className={ 'hidden h-0 w-0 abs' }
18− ref={(ref) => { this.input = ref; }}
19− onChange={this.onChange}
20− disabled={this.props.disabled}
21− type="file"
22− />
23− {this.props.children}
24− </label>
15+ <label className={this.props.className}>
16+ <input
17+ className={'hidden h-0 w-0 abs'}
18+ ref={ref => {
19+ this.input = ref;
20+ }}
21+ onChange={this.onChange}
22+ disabled={this.props.disabled}
23+ type="file"
24+ />
25+ {this.props.children}
26+ </label>
2527 );
2628 }
2729}
src/bridge/components/ValidatedBlockie.js+24−26
@@ -1,6 +1,6 @@
1−import React from 'react'
2−import PropTypes from 'prop-types'
3−import makeBlockie from 'ethereum-blockies-base64'
1+import React from 'react';
2+import PropTypes from 'prop-types';
3+import makeBlockie from 'ethereum-blockies-base64';
44
55const ValidatedBlockie = ({
66 className,
@@ -13,42 +13,42 @@ const ValidatedBlockie = ({
1313 displayPass,
1414 displayFailure,
1515 focussed,
16− beenFocussed
16+ beenFocussed,
1717}) => {
18−
1918 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
2019
2120 return (
2221 <div className={`${className}`} style={style}>
23− { displayPass
24− ? <img
25− alt={ address }
26− src={ makeBlockie(address) }
22+ {displayPass ? (
23+ <img
24+ alt={address}
25+ src={makeBlockie(address)}
2726 style={{
2827 width: size,
2928 height: size,
30− }} />
31− : <div className={''} style={{
32− backgroundColor: colorStyle[1],
33− width: size,
34− height: size,
35− }} />
36− }
37− {
38− children
39− }
29+ }}
30+ />
31+ ) : (
32+ <div
33+ className={''}
34+ style={{
35+ backgroundColor: colorStyle[1],
36+ width: size,
37+ height: size,
38+ }}
39+ />
40+ )}
41+ {children}
4042 </div>
41− )
42−}
43−
43+ );
44+};
4445
4546const styleSelector = (pass, fail, focus) => {
4647 if (fail === true) return ['#FFFFFF', '#F8C134'];
4748 if (pass === true) return ['#FFFFFF', '#2AA779'];
4849 if (focus === true) return ['#FFFFFF', '#4330FC'];
4950 return ['#FFFFFF', '#7F7F7F'];
50−}
51−
51+};
5252
5353ValidatedBlockie.propTypes = {
5454 className: PropTypes.string,
@@ -56,11 +56,9 @@ ValidatedBlockie.propTypes = {
5656 style: PropTypes.object,
5757};
5858
59−
6059ValidatedBlockie.defaultProps = {
6160 className: '',
6261 style: {},
6362};
6463
65−
6664export default ValidatedBlockie;
src/bridge/components/ValidatedSigil.js+33−37
@@ -1,20 +1,19 @@
1−import React from 'react'
2−import PropTypes from 'prop-types'
1+import React from 'react';
2+import PropTypes from 'prop-types';
33
4−import { pour } from 'sigil-js'
5−import * as ob from 'urbit-ob'
6−import ReactSVGComponents from '../components/ReactSVGComponents'
4+import { pour } from 'sigil-js';
5+import * as ob from 'urbit-ob';
6+import ReactSVGComponents from '../components/ReactSVGComponents';
77
88const isValidPatp = patp => {
9− let isValid = false
9+ let isValid = false;
1010 try {
11− isValid = ob.isValidPatp(patp)
12− } catch(err) {
13− return isValid
11+ isValid = ob.isValidPatp(patp);
12+ } catch (err) {
13+ return isValid;
1414 }
15− return isValid
16−}
17−
15+ return isValid;
16+};
1817
1918const ValidatedSigil = ({
2019 className,
@@ -27,41 +26,40 @@ const ValidatedSigil = ({
2726 displayPass,
2827 displayFailure,
2928 focussed,
30− beenFocussed
29+ beenFocussed,
3130}) => {
32−
3331 const colorStyle = styleSelector(displayPass, displayFailure, focussed);
3432
3533 return (
3634 <div className={`${className}`} style={style}>
37− {
38− isValidPatp(patp)
39− ? pour({
40− patp: patp,
41− renderer: ReactSVGComponents,
42− size: size,
43− colorway: colorStyle,
44− margin: margin,
45− })
46− : <div className={'bg-transparent'} style={{
47− width: size,
48− height: size,
49− }} />
50− }
51− {
52− children
53− }
35+ {isValidPatp(patp) ? (
36+ pour({
37+ patp: patp,
38+ renderer: ReactSVGComponents,
39+ size: size,
40+ colorway: colorStyle,
41+ margin: margin,
42+ })
43+ ) : (
44+ <div
45+ className={'bg-transparent'}
46+ style={{
47+ width: size,
48+ height: size,
49+ }}
50+ />
51+ )}
52+ {children}
5453 </div>
55− )
56−}
54+ );
55+};
5756
5857const styleSelector = (pass, fail, focus) => {
5958 if (fail === true) return ['#FFFFFF', '#F8C134'];
6059 if (pass === true) return ['#FFFFFF', '#2AA779'];
6160 if (focus === true) return ['#FFFFFF', '#4330FC'];
6261 return ['#FFFFFF', '#7F7F7F'];
63−}
64−
62+};
6563
6664ValidatedSigil.propTypes = {
6765 className: PropTypes.string,
@@ -69,11 +67,9 @@ ValidatedSigil.propTypes = {
6967 style: PropTypes.object,
7068};
7169
72−
7370ValidatedSigil.defaultProps = {
7471 className: '',
7572 style: {},
7673};
7774
78−
7975export default ValidatedSigil;
src/bridge/components/Warning.js+4−9
@@ -1,16 +1,13 @@
11import React from 'react';
2−import PropTypes from 'prop-types'
2+import PropTypes from 'prop-types';
33
44const Warning = ({ className, children, style, measure }) => {
5−
6−
75 return (
86 <div className={`mb-7 p-4 bg-yellow break-word ${className}`} style={style}>
9− { children }
7+ {children}
108 </div>
11− )
12−}
13−
9+ );
10+};
1411
1512Warning.propTypes = {
1613 className: PropTypes.string,
@@ -18,11 +15,9 @@ Warning.propTypes = {
1815 style: PropTypes.object,
1916};
2017
21−
2218Warning.defaultProps = {
2319 className: '',
2420 style: {},
2521};
2622
27−
2823export default Warning;
src/bridge/components/advancedInput.js+53−59
@@ -14,35 +14,30 @@ const initMessage = data => ({
1414
1515// Validation display triggers
1616const displayTriggers = {
17− eagerSuccess: r => t(r.pass),
18− eagerFailure: r => f(r.pass),
19− tactfulSuccess: r => t(r.pass) && t(r.beenFocussed),
20− tactfulFailure: r => f(r.pass) && t(r.beenFocussed) && f(r.focussed),
21−}
17+ eagerSuccess: r => t(r.pass),
18+ eagerFailure: r => f(r.pass),
19+ tactfulSuccess: r => t(r.pass) && t(r.beenFocussed),
20+ tactfulFailure: r => f(r.pass) && t(r.beenFocussed) && f(r.focussed),
21+};
2222
2323// =============================================================================
2424// AdvancedInput
2525
26−const advancedInput = ({
27− WrappedComponent,
28− validators,
29− transformers,
30−}) => {
31−
26+const advancedInput = ({ WrappedComponent, validators, transformers }) => {
3227 // set argument defaults
33− validators = defaultTo(validators, [])
34− transformers = defaultTo(transformers, [])
28+ validators = defaultTo(validators, []);
29+ transformers = defaultTo(transformers, []);
3530
3631 class AdvancedInputHOC extends Component {
3732 constructor(props) {
38− super(props)
33+ super(props);
3934 this.state = {
4035 focussed: false,
4136 beenFocussed: false,
42− }
43− this.onFocus = this.onFocus.bind(this)
44− this.onBlur = this.onBlur.bind(this)
45− this.onChange = this.onChange.bind(this)
37+ };
38+ this.onFocus = this.onFocus.bind(this);
39+ this.onBlur = this.onBlur.bind(this);
40+ this.onChange = this.onChange.bind(this);
4641 }
4742
4843 onFocus = () => this.setState({ focussed: true });
@@ -50,8 +45,7 @@ const advancedInput = ({
5045 onBlur = () => this.setState({ focussed: false, beenFocussed: true });
5146
5247 onChange = value => {
53−
54− const { props } = this
48+ const { props } = this;
5549
5650 // =======================================================================
5751 // 0. RUN VALUE TRANSFORMERS
@@ -61,14 +55,13 @@ const advancedInput = ({
6155 // should always have a sig prepending them, but users might not know/care
6256 // about such a technicality. ValueTransformers are executed from right to
6357 // left.
64− const newValue = compose(...transformers)(value)
58+ const newValue = compose(...transformers)(value);
6559
66− props.onChange(newValue)
67− }
60+ props.onChange(newValue);
61+ };
6862
6963 render() {
70−
71− const { state, props } = this
64+ const { state, props } = this;
7265
7366 // =======================================================================
7467 // RUN VALIDATORS AND DETERMINE STYLES
@@ -76,16 +69,19 @@ const advancedInput = ({
7669 // Run validation chain on the transformed value. Validators are executed
7770 // from right to left and exit on the first validation failure.
7871
79− const report = compose(...validators, initMessage)(props.value)
72+ const report = compose(
73+ ...validators,
74+ initMessage
75+ )(props.value);
8076
8177 // Makes a uniform data structure to pass to display triggers. This
8278 // removes the need to rely on argument order for these functions.
8379 const stateReport = {
84− pass: report.pass,
80+ pass: report.pass,
8581 beenFocussed: state.beenFocussed,
86− focussed: state.focussed,
87− submitted: props.submitted,
88− }
82+ focussed: state.focussed,
83+ submitted: props.submitted,
84+ };
8985
9086 // Determine if the passing validation style or message should be shown
9187 const displayPass = displayTriggers.eagerSuccess(stateReport);
@@ -95,49 +91,46 @@ const advancedInput = ({
9591
9692 // Generate class strings for validation state. VCN stands for validation
9793 // class names
98− const valClassname = styleSelector(
99− displayPass,
100− displayFailure
101− );
94+ const valClassname = styleSelector(displayPass, displayFailure);
10295
10396 return (
10497 <WrappedComponent
10598 {...props}
10699 // Events
107− onFocus={ this.onFocus }
108− onBlur={ this.onBlur }
109− onChange={ this.onChange }
110−
100+ onFocus={this.onFocus}
101+ onBlur={this.onBlur}
102+ onChange={this.onChange}
111103 // stateReport
112− focussed={ state.focussed }
113− beenFocussed={ state.beenFocussed }
114− displayPass={ displayPass }
115− displayFailure={ displayFailure }
104+ focussed={state.focussed}
105+ beenFocussed={state.beenFocussed}
106+ displayPass={displayPass}
107+ displayFailure={displayFailure}
116108 // 'error' prop refers to an error message string, not the presence
117109 // of an error.
118− error={ report.error }
119− didPass={ report.pass }
110+ error={report.error}
111+ didPass={report.pass}
120112 value={props.value}
121− className={ props.className }
122− disabled={ props.disabled }
113+ className={props.className}
114+ disabled={props.disabled}
123115 valClassname={valClassname}
124− advanced={true} />
125− )
116+ advanced={true}
117+ />
118+ );
126119 }
127120 }
128121
129122 AdvancedInputHOC.propTypes = {
130− className: PropTypes.string,
131− style: PropTypes.object,
132− value: PropTypes.string,
133− submitted: PropTypes.bool,
123+ className: PropTypes.string,
124+ style: PropTypes.object,
125+ value: PropTypes.string,
126+ submitted: PropTypes.bool,
134127 };
135128
136129 AdvancedInputHOC.defaultProps = {
137− className: '',
138− style: {},
139− value: '',
140− submitted: false,
130+ className: '',
131+ style: {},
132+ value: '',
133+ submitted: false,
141134 };
142135
143136 return AdvancedInputHOC;
@@ -145,9 +138,10 @@ const advancedInput = ({
145138};
146139
147140const styleSelector = (pass, fail) => {
148− if (fail === true) return 'br-yellow outline-yellow f-br-yellow bg-yellow-light';
141+ if (fail === true)
142+ return 'br-yellow outline-yellow f-br-yellow bg-yellow-light';
149143 if (pass === true) return 'br-green outline-green f-br-green';
150− return 'br-gray-50 f-br-blue outline-blue'
151−}
144+ return 'br-gray-50 f-br-blue outline-blue';
145+};
152146
153147export default advancedInput;
src/bridge/components/icons/Chevron.js+6−7
@@ -1,16 +1,16 @@
1−import React from 'react'
1+import React from 'react';
22import PropTypes from 'prop-types';
33
44const Chevron = props => {
55 return (
66 <img
77 src={`data:image/svg+xml;utf8,<svg width="11" height="24" viewBox="0 0 11 24" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M10 12L10.387 12.3166L10.646 12L10.387 11.6833L10 12ZM0.613022 1.31662L9.61302 12.3166L10.387 11.6833L1.38698 0.68338L0.613022 1.31662ZM9.61302 11.6833L0.613021 22.6834L1.38698 23.3166L10.387 12.3166L9.61302 11.6833Z" stroke="${props.color}" stroke-width="${props.strokeWidth}" fill="${props.color}"/></svg>`}
8− alt=''
8+ alt=""
99 style={props.style}
1010 className={props.className}
1111 />
12− )
13−}
12+ );
13+};
1414
1515Chevron.propTypes = {
1616 style: PropTypes.object,
@@ -19,12 +19,11 @@ Chevron.propTypes = {
1919 strokeWidth: PropTypes.string,
2020};
2121
22−
2322Chevron.defaultProps = {
2423 style: {},
2524 className: '',
2625 color: 'black',
27− strokeWidth: '1px'
26+ strokeWidth: '1px',
2827};
2928
30−export default Chevron
29+export default Chevron;
src/bridge/lib/base.js+5−8
@@ -1,9 +1,6 @@
1−const getDomProps = props => Object.entries(props).reduce((acc, [k, v]) => {
2− return k.match(/prop-/)
3− ? { ...acc, [k]: v }
4− : acc
5−}, {})
1+const getDomProps = props =>
2+ Object.entries(props).reduce((acc, [k, v]) => {
3+ return k.match(/prop-/) ? { ...acc, [k]: v } : acc;
4+ }, {});
65
7−export {
8− getDomProps
9−}
6+export { getDomProps };
src/bridge/lib/child.js+11−13
@@ -1,20 +1,18 @@
1−const MIN_GALAXY = 0
2−const MIN_STAR = Math.pow(2, 8)
3−const MIN_PLANET = Math.pow(2, 16)
4−const MAX_GALAXY = MIN_STAR - 1
5−const MAX_STAR = MIN_PLANET - 1
1+const MIN_GALAXY = 0;
2+const MIN_STAR = Math.pow(2, 8);
3+const MIN_PLANET = Math.pow(2, 16);
4+const MAX_GALAXY = MIN_STAR - 1;
5+const MAX_STAR = MIN_PLANET - 1;
66
77const randomStarName = galaxy =>
8− (Math.floor(Math.random() * MAX_GALAXY) + 1) * MIN_STAR + galaxy
8+ (Math.floor(Math.random() * MAX_GALAXY) + 1) * MIN_STAR + galaxy;
99
1010const randomPlanetName = star =>
11− (Math.floor(Math.random() * MAX_STAR) + 1) * (MAX_STAR + 1) + star
11+ (Math.floor(Math.random() * MAX_STAR) + 1) * (MAX_STAR + 1) + star;
1212
1313const getSpawnCandidate = point =>
14− point >= MIN_GALAXY && point < MIN_STAR
15− ? randomStarName(point)
16− : randomPlanetName(point)
14+ point >= MIN_GALAXY && point < MIN_STAR
15+ ? randomStarName(point)
16+ : randomPlanetName(point);
1717
18−export {
19− getSpawnCandidate
20−}
18+export { getSpawnCandidate };
src/bridge/lib/contracts.js+5−9
@@ -1,26 +1,22 @@
1−
21const CONTRACT_ADDRESSES = {
32 DEV: {
43 ecliptic: '0x56db68f29203ff44a803faa2404a44ecbb7a7480',
54 azimuth: '0x863d9c2e5c4c133596cfac29d55255f0d0f86381',
65 polls: '0x935452c45eda2958976a429c9733c40302995efd',
7− delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43'
6+ delegatedSending: '0xb71c0b6cee1bcae56dfe95cd9d3e41ddd7eafc43',
87 },
98 ROPSTEN: {
109 ecliptic: '0x8b9f86a28921d9c705b3113a755fb979e1bd1bce',
1110 azimuth: '0x308ab6a6024cf198b57e008d0ac9ad0219886579',
12− polls: '0xf5DA85f0d285A0F88af2388DD177A221872C8971'
11+ polls: '0xf5DA85f0d285A0F88af2388DD177A221872C8971',
1312 //TODO delegatedSending
1413 },
1514 MAINNET: {
1615 ecliptic: '0x6ac07b7c4601b5ce11de8dfe6335b871c7c4dd4d',
1716 azimuth: '0x223c067f8cf28ae173ee5cafea60ca44c335fecb',
18− polls: '0x7fecab617c868bb5996d99d95200d2fa708218e4'
17+ polls: '0x7fecab617c868bb5996d99d95200d2fa708218e4',
1918 //TODO delegatedSending
2019 },
21−}
22−
23−export {
24− CONTRACT_ADDRESSES
25−}
20+};
2621
22+export { CONTRACT_ADDRESSES };
src/bridge/lib/error.js+19−27
@@ -1,4 +1,3 @@
1−
21const BRIDGE_ERROR = {
32 MISSING_WEB3: new Error('no web3 object found'),
43 MISSING_WALLET: new Error('no wallet found'),
@@ -11,36 +10,29 @@ const BRIDGE_ERROR = {
1110 INVALID_ROUTE: new Error('invalid route'),
1211 INVALID_NETWORK_TYPE: new Error('invalid network type'),
1312 INVALID_WALLET_TYPE: new Error('invalid wallet type'),
14−}
13+};
1514
1615const renderTxnError = (web3, msg) => {
1716 const txnCost = (web3, msg) => {
18− const costWei = msg.match(/upfront cost is: ([0-9]+)/)
19− const costEth = web3.utils.fromWei(costWei[1], 'ether')
20− return parseFloat(costEth).toFixed(4)
21− }
17+ const costWei = msg.match(/upfront cost is: ([0-9]+)/);
18+ const costEth = web3.utils.fromWei(costWei[1], 'ether');
19+ return parseFloat(costEth).toFixed(4);
20+ };
2221
2322 const acctBalance = (web3, msg) => {
24− const balWei = msg.match(/sender's account only has: ([0-9]+)/)
25− const balEth = web3.utils.fromWei(balWei[1], 'ether')
26− return parseFloat(balEth).toFixed(4)
27− }
28−
29− return (
30− msg.includes("doesn't have enough funds")
31−
32− ? "The account doesn't have enough Ether. " +
33− `The up-front transaction cost is ${txnCost(web3, msg)} ETH, but the ` +
34− `account only holds ${acctBalance(web3, msg)} ETH.`
23+ const balWei = msg.match(/sender's account only has: ([0-9]+)/);
24+ const balEth = web3.utils.fromWei(balWei[1], 'ether');
25+ return parseFloat(balEth).toFixed(4);
26+ };
3527
36− : "You may want to verify that the account that has signed the " +
37− "transaction holds sufficient Ether to pay for the gas, and " +
38− "also that you haven't set the gas limit for the transaction " +
39− "too low."
40− )
41−}
28+ return msg.includes("doesn't have enough funds")
29+ ? "The account doesn't have enough Ether. " +
30+ `The up-front transaction cost is ${txnCost(web3, msg)} ETH, but the ` +
31+ `account only holds ${acctBalance(web3, msg)} ETH.`
32+ : 'You may want to verify that the account that has signed the ' +
33+ 'transaction holds sufficient Ether to pay for the gas, and ' +
34+ "also that you haven't set the gas limit for the transaction " +
35+ 'too low.';
36+};
4237
43−export {
44− BRIDGE_ERROR,
45− renderTxnError
46−}
38+export { BRIDGE_ERROR, renderTxnError };
src/bridge/lib/invite.js+105−78
@@ -1,69 +1,69 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import Tx from 'ethereumjs-tx'
1+import { Just, Nothing } from 'folktale/maybe';
2+import Tx from 'ethereumjs-tx';
33
4−import * as azimuth from 'azimuth-js'
5−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
6−import * as wg from '../../walletgen/lib/lib.js'
7−import * as tank from './tank'
4+import * as azimuth from 'azimuth-js';
5+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
6+import * as wg from '../../walletgen/lib/lib.js';
7+import * as tank from './tank';
88
9−import JSZip from 'jszip'
10−import saveAs from 'file-saver'
9+import JSZip from 'jszip';
10+import saveAs from 'file-saver';
1111
12−import { sendSignedTransaction } from './txn'
13−import { BRIDGE_ERROR } from '../lib/error'
14−import { attemptSeedDerivation } from './keys'
15−import { addHexPrefix, WALLET_NAMES } from './wallet'
12+import { sendSignedTransaction } from './txn';
13+import { BRIDGE_ERROR } from '../lib/error';
14+import { attemptSeedDerivation } from './keys';
15+import { addHexPrefix, WALLET_NAMES } from './wallet';
1616
1717const INVITE_STAGES = {
18− INVITE_LOGIN: "invite login",
19− INVITE_WALLET: "invite wallet",
20− INVITE_VERIFY: "invite verify",
21− INVITE_TRANSACTIONS: "invite transactions"
22−}
18+ INVITE_LOGIN: 'invite login',
19+ INVITE_WALLET: 'invite wallet',
20+ INVITE_VERIFY: 'invite verify',
21+ INVITE_TRANSACTIONS: 'invite transactions',
22+};
2323
2424const WALLET_STATES = {
25− UNLOCKING: "Unlocking invite wallet",
26− GENERATING: "Generating your wallet",
27− RENDERING: "Creating paper collateral",
28− PAPER_READY: "Download your wallet",
29− DOWNLOADED: "Wallet downloaded",
30− TRANSACTIONS: "Sending transactions"
31−}
25+ UNLOCKING: 'Unlocking invite wallet',
26+ GENERATING: 'Generating your wallet',
27+ RENDERING: 'Creating paper collateral',
28+ PAPER_READY: 'Download your wallet',
29+ DOWNLOADED: 'Wallet downloaded',
30+ TRANSACTIONS: 'Sending transactions',
31+};
3232
3333const TRANSACTION_STATES = {
3434 GENERATING: {
3535 label: 'Generating transactions',
36− pct: "0%"
36+ pct: '0%',
3737 },
3838 SIGNING: {
3939 label: 'Signing transactions',
40− pct: "15%"
40+ pct: '15%',
4141 },
4242 FUNDING_INVITE: {
4343 label: 'Funding invite wallet',
44− pct: "30%"
44+ pct: '30%',
4545 },
4646 CLAIMING: {
4747 label: 'Claiming invite',
48− pct: "55%"
48+ pct: '55%',
4949 },
5050 FUNDING_RECIPIENT: {
5151 label: 'Funding recipient wallet',
52− pct: "65%"
52+ pct: '65%',
5353 },
5454 CONFIGURING: {
5555 label: 'Configuring planet',
56− pct: "85%"
56+ pct: '85%',
5757 },
5858 CLEANING: {
5959 label: 'Cleaning up',
60− pct: "95%"
60+ pct: '95%',
6161 },
6262 DONE: {
6363 label: 'Done',
64− pct: "100%"
64+ pct: '100%',
6565 },
66−}
66+};
6767
6868async function generateWallet(point) {
6969 const ticket = await wg.makeTicket(point);
@@ -83,35 +83,42 @@ async function downloadWallet(paper) {
8383 const bin3 = paper.filter(item => item.bin === '3');
8484 const bin4 = paper.filter(item => item.bin === '4');
8585
86− const bin0Folder = zip.folder("0. Public");
87− const bin1Folder = zip.folder("1. Very High Friction Custody");
88− const bin2Folder = zip.folder("2. High Friction Custody");
89− const bin3Folder = zip.folder("3. Medium Friction Custody");
90− const bin4Folder = zip.folder("4. Low Friction Custody");
86+ const bin0Folder = zip.folder('0. Public');
87+ const bin1Folder = zip.folder('1. Very High Friction Custody');
88+ const bin2Folder = zip.folder('2. High Friction Custody');
89+ const bin3Folder = zip.folder('3. Medium Friction Custody');
90+ const bin4Folder = zip.folder('4. Low Friction Custody');
9191
92− bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png))
92+ bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
9393 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
9494 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
9595 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
9696 bin4.forEach(item => bin4Folder.file(`${item.pageTitle}.png`, item.png));
9797
98− zip.generateAsync({type:"blob"}).then((content) => {
98+ zip.generateAsync({ type: 'blob' }).then(content => {
9999 saveAs(content, 'urbit-wallet.zip');
100− resolve(true)
100+ resolve(true);
101101 });
102− })
102+ });
103103}
104104
105105async function startTransactions(args) {
106− let { realPointM, web3M, contractsM,
107− inviteWalletM, realWalletM, setUrbitWallet, updateProgress } = args
106+ let {
107+ realPointM,
108+ web3M,
109+ contractsM,
110+ inviteWalletM,
111+ realWalletM,
112+ setUrbitWallet,
113+ updateProgress,
114+ } = args;
108115
109116 const askForFunding = (address, amount, current) => {
110117 updateProgress({
111− type: "notify",
112− value: `Please make sure ${address} has at least ${amount} wei, we'll continue once that's true. Current balance: ${current}. Waiting`
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`,
113120 });
114− }
121+ };
115122
116123 if (Nothing.hasInstance(web3M)) {
117124 throw BRIDGE_ERROR.MISSING_WEB3;
@@ -145,7 +152,11 @@ async function startTransactions(args) {
145152
146153 // transfer from invite wallet to new wallet
147154
148− let transferTx = azimuth.ecliptic.transferPoint(contracts, point, realWallet.ownership.keys.address);
155+ let transferTx = azimuth.ecliptic.transferPoint(
156+ contracts,
157+ point,
158+ realWallet.ownership.keys.address
159+ );
149160 transferTx.gas = 500000; //TODO can maybe be lower?
150161
151162 // ping gas tank with txs if needed
@@ -158,20 +169,25 @@ async function startTransactions(args) {
158169 //NOTE using web3.eth.accounts.signTransaction is broken (1.0.0-beta51)
159170 let transferStx = new Tx(transferTx);
160171 transferStx.sign(inviteWallet.privateKey);
161− let rawTransferStx = '0x'+transferStx.serialize().toString('hex');
172+ let rawTransferStx = '0x' + transferStx.serialize().toString('hex');
162173
163174 const transferCost = transferTx.gas * transferTx.gasPrice;
164175 updateProgress({
165− type: "progress",
166− value: TRANSACTION_STATES.FUNDING_INVITE
176+ type: 'progress',
177+ value: TRANSACTION_STATES.FUNDING_INVITE,
167178 });
168179 let usedTank = await tank.ensureFundsFor(
169− web3, point, inviteAddress, transferCost, [rawTransferStx], askForFunding
180+ web3,
181+ point,
182+ inviteAddress,
183+ transferCost,
184+ [rawTransferStx],
185+ askForFunding
170186 );
171187
172188 updateProgress({
173− type: "progress",
174− value: TRANSACTION_STATES.CLAIMING
189+ type: 'progress',
190+ value: TRANSACTION_STATES.CLAIMING,
175191 });
176192
177193 // send transaction
@@ -186,7 +202,11 @@ async function startTransactions(args) {
186202
187203 // configure management proxy
188204
189− let managementTx = azimuth.ecliptic.setManagementProxy(contracts, point, realWallet.management.keys.address);
205+ let managementTx = azimuth.ecliptic.setManagementProxy(
206+ contracts,
207+ point,
208+ realWallet.management.keys.address
209+ );
190210 managementTx.gas = 200000;
191211 managementTx.nonce = 0;
192212
@@ -196,7 +216,7 @@ async function startTransactions(args) {
196216 walletType: WALLET_NAMES.TICKET,
197217 urbitWallet: Just(realWallet),
198218 pointCursor: Just(point),
199− pointCache: {[point]: {keyRevisionNumber: 0}}
219+ pointCache: { [point]: { keyRevisionNumber: 0 } },
200220 });
201221 if (Nothing.hasInstance(networkSeed)) {
202222 throw new Error('wtf network seed not derived');
@@ -221,34 +241,39 @@ async function startTransactions(args) {
221241 txs = txs.map(tx => {
222242 tx.gasPrice = 20000000000;
223243 tx.from = newAddress;
224− totalCost = totalCost + (tx.gasPrice * tx.gas);
244+ totalCost = totalCost + tx.gasPrice * tx.gas;
225245 return tx;
226246 });
227247
228248 let rawStxs = txs.map(tx => {
229249 let stx = new Tx(tx);
230250 stx.sign(Buffer.from(realWallet.ownership.keys.private, 'hex'));
231− return '0x'+stx.serialize().toString('hex');
251+ return '0x' + stx.serialize().toString('hex');
232252 });
233253
234254 updateProgress({
235− type: "progress",
236− value: TRANSACTION_STATES.FUNDING_RECIPIENT
255+ type: 'progress',
256+ value: TRANSACTION_STATES.FUNDING_RECIPIENT,
237257 });
238258 usedTank = await tank.ensureFundsFor(
239− web3, point, newAddress, totalCost, rawStxs, askForFunding
259+ web3,
260+ point,
261+ newAddress,
262+ totalCost,
263+ rawStxs,
264+ askForFunding
240265 );
241266
242267 updateProgress({
243− type: "progress",
244− value: TRANSACTION_STATES.CONFIGURING
268+ type: 'progress',
269+ value: TRANSACTION_STATES.CONFIGURING,
245270 });
246271
247272 await sendAndAwaitConfirm(web3, rawStxs, usedTank);
248273
249274 updateProgress({
250− type: "progress",
251− value: TRANSACTION_STATES.CLEANING
275+ type: 'progress',
276+ value: TRANSACTION_STATES.CLEANING,
252277 });
253278
254279 // if non-trivial eth left in invite wallet, transfer to new ownership
@@ -257,18 +282,18 @@ async function startTransactions(args) {
257282 const gasLimit = 21000;
258283 const sendEthCost = gasPrice * gasLimit;
259284 if (balance > sendEthCost) {
260− const value = (balance - sendEthCost);
285+ const value = balance - sendEthCost;
261286 console.log('sending', value);
262287 const tx = {
263288 to: newAddress,
264289 value: value,
265290 gasPrice: gasPrice,
266291 gas: gasLimit,
267− nonce: inviteNonce++
268− }
292+ nonce: inviteNonce++,
293+ };
269294 let stx = new Tx(tx);
270295 stx.sign(inviteWallet.privateKey);
271− const rawTx = '0x'+stx.serialize().toString('hex');
296+ const rawTx = '0x' + stx.serialize().toString('hex');
272297 web3.eth.sendSignedTransaction(rawTx).catch(err => {
273298 console.log('error sending value tx, who cares', err);
274299 });
@@ -276,8 +301,8 @@ async function startTransactions(args) {
276301 }
277302
278303 updateProgress({
279− type: "progress",
280− value: TRANSACTION_STATES.DONE
304+ type: 'progress',
305+ value: TRANSACTION_STATES.DONE,
281306 });
282307
283308 // proceed without waiting for confirm
@@ -285,11 +310,13 @@ async function startTransactions(args) {
285310}
286311
287312async function sendAndAwaitConfirm(web3, signedTxs, usedTank) {
288− await Promise.all(signedTxs.map(tx => {
289− return new Promise((resolve, reject) => {
290− sendSignedTransaction(web3, tx, usedTank, resolve);
291− });
292− }));
313+ await Promise.all(
314+ signedTxs.map(tx => {
315+ return new Promise((resolve, reject) => {
316+ sendSignedTransaction(web3, tx, usedTank, resolve);
317+ });
318+ })
319+ );
293320}
294321
295322export {
@@ -298,5 +325,5 @@ export {
298325 startTransactions,
299326 INVITE_STAGES,
300327 WALLET_STATES,
301− TRANSACTION_STATES
302−}
328+ TRANSACTION_STATES,
329+};
src/bridge/lib/inviteMail.js+13−17
@@ -2,7 +2,7 @@
22// did not succeed", you might need to visit localhost:3001 or whatever
33// explicitly and tell your browser that's safe to access.
44// https://stackoverflow.com/a/53011185/1334324
5−const baseUrl = 'https://localhost:3002'
5+const baseUrl = 'https://localhost:3002';
66
77function sendRequest(where, what) {
88 return new Promise((resolve, reject) => {
@@ -10,23 +10,23 @@ function sendRequest(where, what) {
1010 method: 'POST',
1111 cache: 'no-cache',
1212 headers: {
13− 'Content-Type': 'application/json'
13+ 'Content-Type': 'application/json',
1414 },
15− body: JSON.stringify(what)
15+ body: JSON.stringify(what),
1616 })
17− .then(response => {
18− if (response.ok) {
19− resolve(response.json());
20− } else {
21− reject(response);
22− }
23− })
24− .catch(reject);
17+ .then(response => {
18+ if (response.ok) {
19+ resolve(response.json());
20+ } else {
21+ reject(response);
22+ }
23+ })
24+ .catch(reject);
2525 });
2626}
2727
2828async function hasReceived(recipient) {
29− const res = await sendRequest('/has-received', {recipient});
29+ const res = await sendRequest('/has-received', { recipient });
3030 return res.hasReceived;
3131}
3232
@@ -34,8 +34,4 @@ function sendMail(recipient, ticket, tx) {
3434 return sendRequest('/send-ticket', { recipient, ticket, tx });
3535}
3636
37−export {
38− hasReceived,
39− sendMail
40−}
41−
37+export { hasReceived, sendMail };
src/bridge/lib/keys.js+58−65
@@ -1,27 +1,25 @@
1−import BN from 'bn.js'
2−import Maybe from 'folktale/maybe'
3−import * as need from '../lib/need'
1+import BN from 'bn.js';
2+import Maybe from 'folktale/maybe';
3+import * as need from '../lib/need';
44
5−import * as noun from '../nockjs/noun'
6−import * as serial from '../nockjs/serial'
7−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
5+import * as noun from '../nockjs/noun';
6+import * as serial from '../nockjs/serial';
7+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
88
9−import { BRIDGE_ERROR } from './error'
10−import {
11− WALLET_NAMES,
12− eqAddr
13− } from './wallet'
9+import { BRIDGE_ERROR } from './error';
10+import { WALLET_NAMES, eqAddr } from './wallet';
1411
1512// ctsy joemfb
1613const b64 = buf => {
17− let hex = buf.reverse().toString('hex')
18− let n = new BN(hex, 'hex')
19− let c = []
14+ let hex = buf.reverse().toString('hex');
15+ let n = new BN(hex, 'hex');
16+ let c = [];
2017 while (1 === n.cmpn(0)) {
21− c.push(n.andln(0x3f))
22− n = n.shrn(6)
18+ c.push(n.andln(0x3f));
19+ n = n.shrn(6);
2320 }
2421
22+ // prettier-ignore
2523 const trans = j =>
2624 10 > j
2725 ? j + 48
@@ -33,38 +31,44 @@ const b64 = buf => {
3331 ? 45
3432 : 126
3533
36− return '0w' +
37− c.reduce((a, b, i) =>
38− String.fromCharCode(trans(b)) +
39− (( i && 0 === i % 5 ) ? '.' : '') + a,
40− '')
41−}
34+ return (
35+ '0w' +
36+ c.reduce(
37+ (a, b, i) =>
38+ String.fromCharCode(trans(b)) + (i && 0 === i % 5 ? '.' : '') + a,
39+ ''
40+ )
41+ );
42+};
4243
4344const jam = seed => {
44− const hex = serial.jam(seed).toString().slice(2)
45− const pad = hex.length % 2 === 0 ? hex : '0' + hex
46− return Buffer.from(pad, 'hex').reverse()
47−}
45+ const hex = serial
46+ .jam(seed)
47+ .toString()
48+ .slice(2);
49+ const pad = hex.length % 2 === 0 ? hex : '0' + hex;
50+ return Buffer.from(pad, 'hex').reverse();
51+};
4852
4953const genKey = (networkSeed, point, revision) => {
50− const pair = kg.deriveNetworkKeys(networkSeed)
51− const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex') // '42' is curve parameter
54+ const pair = kg.deriveNetworkKeys(networkSeed);
55+ const bnsec = new BN(pair.crypt.private + pair.auth.private + '42', 'hex'); // '42' is curve parameter
5256
5357 const sed = noun.dwim(
5458 noun.Atom.fromString(point),
5559 noun.Atom.fromInt(revision),
5660 noun.Atom.fromString(bnsec.toString()),
5761 noun.Atom.yes
58− )
62+ );
5963
60− return b64(jam(sed))
61−}
64+ return b64(jam(sed));
65+};
6266
6367// 'next' refers to setting the next set of keys
6468// if false, we use revision - 1
6569const attemptSeedDerivation = async (next, args) => {
66− const { walletType } = args
67− const { urbitWallet, authMnemonic } = args
70+ const { walletType } = args;
71+ const { urbitWallet, authMnemonic } = args;
6872
6973 // NB (jtobin):
7074 //
@@ -74,64 +78,53 @@ const attemptSeedDerivation = async (next, args) => {
7478 const pointDetails = need.fromPointCache(args, point);
7579
7680 const revision =
77− next === true
78− ? parseInt(pointDetails.keyRevisionNumber)
79− : parseInt(pointDetails.keyRevisionNumber) - 1
81+ next === true
82+ ? parseInt(pointDetails.keyRevisionNumber)
83+ : parseInt(pointDetails.keyRevisionNumber) - 1;
8084
81− let managementSeed = ''
85+ let managementSeed = '';
8286
83− const ticketLike = [ WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS ]
87+ const ticketLike = [WALLET_NAMES.TICKET, WALLET_NAMES.SHARDS];
8488
8589 if (ticketLike.includes(walletType)) {
86−
8790 const uwal = urbitWallet.matchWith({
8891 Just: uw => uw.value,
8992 Nothing: _ => {
90− throw BRIDGE_ERROR.MISSING_URBIT_WALLET
91− }
92− })
93−
94− managementSeed = uwal.management.seed
93+ throw BRIDGE_ERROR.MISSING_URBIT_WALLET;
94+ },
95+ });
9596
97+ managementSeed = uwal.management.seed;
9698 } else if (walletType === WALLET_NAMES.MNEMONIC) {
97−
9899 const walProxy = need.address(args);
99100
100101 const mnemonic = authMnemonic.matchWith({
101102 Just: mnem => mnem.value,
102103 Nothing: _ => {
103− throw BRIDGE_ERROR.MISSING_MNEMONIC
104− }
105− })
104+ throw BRIDGE_ERROR.MISSING_MNEMONIC;
105+ },
106+ });
106107
107− const chainProxy = pointDetails.managementProxy
108+ const chainProxy = pointDetails.managementProxy;
108109
109110 // the network seed is only derivable from mnemonic if the derived
110111 // management seed equals the record we have on chain
111− const networkSeedDerivable = eqAddr(walProxy, chainProxy)
112+ const networkSeedDerivable = eqAddr(walProxy, chainProxy);
112113
113114 if (networkSeedDerivable === true) {
114− managementSeed = mnemonic
115+ managementSeed = mnemonic;
115116 }
116117 }
117118
118− let networkSeed = Maybe.Nothing()
119+ let networkSeed = Maybe.Nothing();
119120
120121 if (managementSeed !== '') {
121− const seed =
122− await kg.deriveNetworkSeed(managementSeed, '', revision)
122+ const seed = await kg.deriveNetworkSeed(managementSeed, '', revision);
123123
124− networkSeed = Maybe.Just(seed)
124+ networkSeed = Maybe.Just(seed);
125125 }
126126
127− return networkSeed
128−}
129−
130−
131−
132−
133−export {
134− genKey,
135− attemptSeedDerivation
136−}
127+ return networkSeed;
128+};
137129
130+export { genKey, attemptSeedDerivation };
src/bridge/lib/ledger.js+16−23
@@ -1,31 +1,24 @@
1−import Transport from '@ledgerhq/hw-transport-u2f'
2−import Eth from '@ledgerhq/hw-app-eth'
1+import Transport from '@ledgerhq/hw-transport-u2f';
2+import Eth from '@ledgerhq/hw-app-eth';
33
4−const LEDGER_LEGACY_PATH = "m/44'/60'/0'/x"
5−const LEDGER_LIVE_PATH = "m/44'/60'/x'/0/0"
4+const LEDGER_LEGACY_PATH = "m/44'/60'/0'/x";
5+const LEDGER_LIVE_PATH = "m/44'/60'/x'/0/0";
66
7−const chopHdPrefix = str =>
8− str.slice(0, 2) === "m/"
9− ? str.slice(2)
10− : str
7+const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
118
129const ledgerSignTransaction = async (txn, hdpath) => {
13− const transport = await Transport.create()
14− const eth = new Eth(transport)
10+ const transport = await Transport.create();
11+ const eth = new Eth(transport);
1512
16− const path = chopHdPrefix(hdpath)
17− const serializedTx = txn.serialize().toString('hex')
18− const sig = await eth.signTransaction(path, serializedTx)
13+ const path = chopHdPrefix(hdpath);
14+ const serializedTx = txn.serialize().toString('hex');
15+ const sig = await eth.signTransaction(path, serializedTx);
1916
20− txn.v = Buffer.from(sig.v, 'hex')
21− txn.r = Buffer.from(sig.r, 'hex')
22− txn.s = Buffer.from(sig.s, 'hex')
17+ txn.v = Buffer.from(sig.v, 'hex');
18+ txn.r = Buffer.from(sig.r, 'hex');
19+ txn.s = Buffer.from(sig.s, 'hex');
2320
24− return txn
25−}
21+ return txn;
22+};
2623
27−export {
28− LEDGER_LIVE_PATH,
29− LEDGER_LEGACY_PATH,
30− ledgerSignTransaction
31−}
24+export { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH, ledgerSignTransaction };
src/bridge/lib/lib.js+19−18
@@ -1,6 +1,7 @@
1−import * as ob from 'urbit-ob'
1+import * as ob from 'urbit-ob';
22
3−const compose = (...fs) => fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v)
3+const compose = (...fs) =>
4+ fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v);
45
56const allFalse = (...args) => args.every(a => a === false);
67
@@ -8,7 +9,7 @@ const isLast = (l, i) => i === l - 1;
89
910const isUndefined = v => v === undefined;
1011
11−const defaultTo = (v, d) => isUndefined(v) ? d : v;
12+const defaultTo = (v, d) => (isUndefined(v) ? d : v);
1213
1314const seq = n => Array.from(Array(n), (_, i) => i);
1415
@@ -20,23 +21,23 @@ const t = v => v === true;
2021const f = v => v === false;
2122
2223const isValidGalaxy = name => {
23− let point
24+ let point;
2425 try {
25− point = parseInt(ob.patp2dec(name), 10)
26− } catch(err) {
27− return false
26+ point = parseInt(ob.patp2dec(name), 10);
27+ } catch (err) {
28+ return false;
2829 }
29− return point >= 0 && point < 256
30−}
30+ return point >= 0 && point < 256;
31+};
3132
32−const randomPatq = (len) => {
33− let bytes = window.crypto.getRandomValues(new Uint8Array(len))
34− let hex = bytes.reduce((acc, byt) =>
35− acc + byt.toString(16).padStart(2, '0'),
33+const randomPatq = len => {
34+ let bytes = window.crypto.getRandomValues(new Uint8Array(len));
35+ let hex = bytes.reduce(
36+ (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
3637 ''
37− )
38− return ob.hex2patq(hex)
39−}
38+ );
39+ return ob.hex2patq(hex);
40+};
4041
4142export {
4243 compose,
@@ -49,5 +50,5 @@ export {
4950 t,
5051 f,
5152 isValidGalaxy,
52− randomPatq
53−}
53+ randomPatq,
54+};
src/bridge/lib/need.js+42−29
@@ -2,32 +2,44 @@
22//
33// generally, pass the props object into these functions
44
5−import { BRIDGE_ERROR } from '../lib/error'
5+import { BRIDGE_ERROR } from '../lib/error';
66
77function web3(obj) {
8− return ('web3' in obj === false)
9− ? (() => { throw BRIDGE_ERROR.MISSING_WEB3 })()
8+ return 'web3' in obj === false
9+ ? (() => {
10+ throw BRIDGE_ERROR.MISSING_WEB3;
11+ })()
1012 : obj.web3.matchWith({
11− Nothing: () => { throw BRIDGE_ERROR.MISSING_WEB3 },
12− Just: w => w.value
13+ Nothing: () => {
14+ throw BRIDGE_ERROR.MISSING_WEB3;
15+ },
16+ Just: w => w.value,
1317 });
1418}
1519
1620function contracts(obj) {
17− return ('contracts' in obj === false)
18− ? (() => { throw BRIDGE_ERROR.MISSING_CONTRACTS })()
21+ return 'contracts' in obj === false
22+ ? (() => {
23+ throw BRIDGE_ERROR.MISSING_CONTRACTS;
24+ })()
1925 : obj.contracts.matchWith({
20− Nothing: () => { throw BRIDGE_ERROR.MISSING_CONTRACTS },
21− Just: c => c.value
26+ Nothing: () => {
27+ throw BRIDGE_ERROR.MISSING_CONTRACTS;
28+ },
29+ Just: c => c.value,
2230 });
2331}
2432
2533function wallet(obj) {
26− return ('wallet' in obj === false)
27− ? (() => { throw BRIDGE_ERROR.MISSING_WALLET })()
34+ return 'wallet' in obj === false
35+ ? (() => {
36+ throw BRIDGE_ERROR.MISSING_WALLET;
37+ })()
2838 : obj.wallet.matchWith({
29− Nothing: () => { throw BRIDGE_ERROR.MISSING_WALLET },
30− Just: w => w.value
39+ Nothing: () => {
40+ throw BRIDGE_ERROR.MISSING_WALLET;
41+ },
42+ Just: w => w.value,
3143 });
3244}
3345
@@ -36,27 +48,28 @@ function address(obj) {
3648}
3749
3850function pointCursor(obj) {
39− return ('pointCursor' in obj === false)
40− ? (() => { throw BRIDGE_ERROR.MISSING_POINT })()
51+ return 'pointCursor' in obj === false
52+ ? (() => {
53+ throw BRIDGE_ERROR.MISSING_POINT;
54+ })()
4155 : obj.pointCursor.matchWith({
42− Nothing: () => { throw BRIDGE_ERROR.MISSING_POINT },
43− Just: p => p.value
56+ Nothing: () => {
57+ throw BRIDGE_ERROR.MISSING_POINT;
58+ },
59+ Just: p => p.value,
4460 });
4561}
4662
4763function fromPointCache(obj, point) {
48− return ('pointCache' in obj === false)
49− ? (() => { throw BRIDGE_ERROR.MISSING_POINT })()
64+ return 'pointCache' in obj === false
65+ ? (() => {
66+ throw BRIDGE_ERROR.MISSING_POINT;
67+ })()
5068 : point in obj.pointCache
51− ? obj.pointCache[point]
52− : (() => { throw BRIDGE_ERROR.MISSING_POINT })()
69+ ? obj.pointCache[point]
70+ : (() => {
71+ throw BRIDGE_ERROR.MISSING_POINT;
72+ })();
5373}
5474
55−export {
56− web3,
57− contracts,
58− wallet,
59− address,
60− pointCursor,
61− fromPointCache
62−}
75+export { web3, contracts, wallet, address, pointCursor, fromPointCache };
src/bridge/lib/network.js+12−15
@@ -3,20 +3,17 @@ const NETWORK_NAMES = {
33 LOCAL: Symbol('LOCAL'),
44 ROPSTEN: Symbol('ROPSTEN'),
55 MAINNET: Symbol('MAINNET'),
6−}
6+};
77
8−const renderNetworkType = (network) =>
9− network === NETWORK_NAMES.OFFLINE
10− ? 'Offline'
11− : network === NETWORK_NAMES.ROPSTEN
12− ? 'Ropsten'
13− : network === NETWORK_NAMES.MAINNET
14− ? 'Main Network'
15− : network === NETWORK_NAMES.LOCAL
16− ? 'Local Node'
17− : 'Offline'
8+const renderNetworkType = network =>
9+ network === NETWORK_NAMES.OFFLINE
10+ ? 'Offline'
11+ : network === NETWORK_NAMES.ROPSTEN
12+ ? 'Ropsten'
13+ : network === NETWORK_NAMES.MAINNET
14+ ? 'Main Network'
15+ : network === NETWORK_NAMES.LOCAL
16+ ? 'Local Node'
17+ : 'Offline';
1818
19−export {
20− NETWORK_NAMES,
21− renderNetworkType
22−}
19+export { NETWORK_NAMES, renderNetworkType };
src/bridge/lib/proxy.js+10−15
@@ -1,21 +1,16 @@
1−
21const PROXY_TYPE = {
32 MANAGEMENT_PROXY: Symbol('MANAGEMENT_PROXY'),
43 SPAWN_PROXY: Symbol('SPAWN_PROXY'),
54 TRANSFER_PROXY: Symbol('TRANSFER_PROXY'),
6−}
7−
8−const renderProxyType = (proxyType) =>
9− proxyType === PROXY_TYPE.MANAGEMENT_PROXY
10− ? 'management'
11− : proxyType === PROXY_TYPE.SPAWN_PROXY
12− ? 'spawn'
13− : proxyType === PROXY_TYPE.TRANSFER_PROXY
14− ? 'transfer'
15− : 'proxy'
5+};
166
17−export {
18− PROXY_TYPE,
19− renderProxyType
20−}
7+const renderProxyType = proxyType =>
8+ proxyType === PROXY_TYPE.MANAGEMENT_PROXY
9+ ? 'management'
10+ : proxyType === PROXY_TYPE.SPAWN_PROXY
11+ ? 'spawn'
12+ : proxyType === PROXY_TYPE.TRANSFER_PROXY
13+ ? 'transfer'
14+ : 'proxy';
2115
16+export { PROXY_TYPE, renderProxyType };
src/bridge/lib/router.js+84−115
@@ -1,43 +1,43 @@
1−import * as ob from 'urbit-ob'
2−import * as need from '../lib/need'
1+import * as ob from 'urbit-ob';
2+import * as need from '../lib/need';
33
44import React from 'react';
55
6−import Landing from '../views/Landing'
7−
8−import InviteTicket from '../views/InviteTicket'
9−
10−import InvitesSend from '../views/InvitesSend.js'
11−import InvitesManage from '../views/InvitesManage.js'
12−
13−import AcceptTransfer from '../views/AcceptTransfer'
14−import CancelTransfer from '../views/CancelTransfer'
15−import CreateGalaxy from '../views/CreateGalaxy'
16−import GenKeyfile from '../views/GenKeyfile'
17−import IssueChild from '../views/IssueChild'
18−import Network from '../views/Network'
19−import Mnemonic from '../views/Mnemonic'
20−import Ledger from '../views/Ledger'
21−import Trezor from '../views/Trezor'
22−import PrivateKey from '../views/PrivateKey'
23−import Keystore from '../views/Keystore'
24−import ViewPoint from '../views/ViewPoint'
25−import SentTransaction from '../views/SentTransaction'
26−import SetKeys from '../views/SetKeys'
6+import Landing from '../views/Landing';
7+
8+import InviteTicket from '../views/InviteTicket';
9+
10+import InvitesSend from '../views/InvitesSend.js';
11+import InvitesManage from '../views/InvitesManage.js';
12+
13+import AcceptTransfer from '../views/AcceptTransfer';
14+import CancelTransfer from '../views/CancelTransfer';
15+import CreateGalaxy from '../views/CreateGalaxy';
16+import GenKeyfile from '../views/GenKeyfile';
17+import IssueChild from '../views/IssueChild';
18+import Network from '../views/Network';
19+import Mnemonic from '../views/Mnemonic';
20+import Ledger from '../views/Ledger';
21+import Trezor from '../views/Trezor';
22+import PrivateKey from '../views/PrivateKey';
23+import Keystore from '../views/Keystore';
24+import ViewPoint from '../views/ViewPoint';
25+import SentTransaction from '../views/SentTransaction';
26+import SetKeys from '../views/SetKeys';
2727import {
2828 SetManagementProxy,
2929 SetSpawnProxy,
30− SetTransferProxy
31− } from '../views/SetProxy'
32−import Point from '../views/Point'
33−import Points from '../views/Points'
34−import Shards from '../views/Shards'
35−import Ticket from '../views/Ticket'
36−import Transfer from '../views/Transfer'
37−import Wallet from '../views/Wallet'
38−
39−import { EthereumWallet } from './wallet'
40−import { renderNetworkType } from './network'
30+ SetTransferProxy,
31+} from '../views/SetProxy';
32+import Point from '../views/Point';
33+import Points from '../views/Points';
34+import Shards from '../views/Shards';
35+import Ticket from '../views/Ticket';
36+import Transfer from '../views/Transfer';
37+import Wallet from '../views/Wallet';
38+
39+import { EthereumWallet } from './wallet';
40+import { renderNetworkType } from './network';
4141
4242const ROUTE_NAMES = {
4343 DEFAULT: Symbol('DEFAULT'),
@@ -70,144 +70,113 @@ const ROUTE_NAMES = {
7070 SET_KEYS: Symbol('SET_KEYS'),
7171 TRANSFER: Symbol('TRANSFER'),
7272 SENT_TRANSACTION: Symbol('SENT_TRANSACTION'),
73− GEN_KEYFILE: Symbol('GEN_KEYFILE')
74−}
73+ GEN_KEYFILE: Symbol('GEN_KEYFILE'),
74+};
7575
7676const createRoutes = () => {
77− const routes = {}
78− routes[ROUTE_NAMES.DEFAULT] = Landing
77+ const routes = {};
78+ routes[ROUTE_NAMES.DEFAULT] = Landing;
7979 //
80− routes[ROUTE_NAMES.INVITE_TICKET] = InviteTicket
81− routes[ROUTE_NAMES.INVITES_SEND] = InvitesSend
82− routes[ROUTE_NAMES.INVITES_MANAGE] = InvitesManage
80+ routes[ROUTE_NAMES.INVITE_TICKET] = InviteTicket;
81+ routes[ROUTE_NAMES.INVITES_SEND] = InvitesSend;
82+ routes[ROUTE_NAMES.INVITES_MANAGE] = InvitesManage;
8383 //
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()
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();
112112
113113const renderRoute = (props, route) => {
114− const { wallet, networkType } = props
115− return (
116− route === ROUTE_NAMES.LANDING
114+ const { wallet, networkType } = props;
115+ return route === ROUTE_NAMES.LANDING
117116 ? 'Bridge'
118−
119117 : route === ROUTE_NAMES.INVITE_TICKET
120118 ? 'Invite code'
121−
122119 : route === ROUTE_NAMES.INVITE_TRANSACTIONS
123120 ? 'Setting up new wallet'
124−
125121 : route === ROUTE_NAMES.INVITES_SEND
126122 ? 'Send invites'
127−
128123 : route === ROUTE_NAMES.INVITES_MANAGE
129124 ? 'Manage invites'
130−
131125 : route === ROUTE_NAMES.NETWORK
132126 ? `${renderNetworkType(networkType)}`
133−
134127 : route === ROUTE_NAMES.WALLET
135128 ? wallet.matchWith({
136129 Nothing: () => 'Wallet',
137− Just: (wal) =>
138− wal.value instanceof EthereumWallet
139− ? (<span className="text-mono">{wal.value.address}</span>)
140− : (<span className="text-mono">{wal.value.address}</span>)
130+ Just: wal =>
131+ wal.value instanceof EthereumWallet ? (
132+ <span className="text-mono">{wal.value.address}</span>
133+ ) : (
134+ <span className="text-mono">{wal.value.address}</span>
135+ ),
141136 })
142−
143137 : route === ROUTE_NAMES.MNEMONIC
144138 ? 'Mnemonic'
145−
146139 : route === ROUTE_NAMES.TICKET
147140 ? 'Urbit Ticket'
148−
149141 : route === ROUTE_NAMES.SHARDS
150142 ? 'Urbit Ticket'
151−
152143 : route === ROUTE_NAMES.LEDGER
153144 ? 'Ledger'
154−
155145 : route === ROUTE_NAMES.TREZOR
156146 ? 'Trezor'
157−
158147 : route === ROUTE_NAMES.PRIVATE_KEY
159148 ? 'Private Key'
160−
161149 : route === ROUTE_NAMES.KEYSTORE
162150 ? 'Keystore File'
163−
164151 : route === ROUTE_NAMES.SHIPS
165152 ? 'Points'
166−
167153 : route === ROUTE_NAMES.VIEW_SHIP
168154 ? 'View'
169−
170155 : route === ROUTE_NAMES.SHIP
171156 ? ob.patp(need.pointCursor(props))
172−
173− : route === ROUTE_NAMES.SET_TRANSFER_PROXY
174− || route === ROUTE_NAMES.SET_MANAGEMENT_PROXY
175− || route === ROUTE_NAMES.SET_SPAWN_PROXY
157+ : route === ROUTE_NAMES.SET_TRANSFER_PROXY ||
158+ route === ROUTE_NAMES.SET_MANAGEMENT_PROXY ||
159+ route === ROUTE_NAMES.SET_SPAWN_PROXY
176160 ? 'Set Proxy'
177−
178161 : route === ROUTE_NAMES.CREATE_GALAXY
179162 ? 'Create Galaxy'
180−
181163 : route === ROUTE_NAMES.ISSUE_CHILD
182164 ? 'Issue Child'
183−
184165 : route === ROUTE_NAMES.TRANSFER
185166 ? 'Transfer'
186−
187167 : route === ROUTE_NAMES.ACCEPT_TRANSFER
188168 ? 'Accept Transfer'
189−
190169 : route === ROUTE_NAMES.CANCEL_TRANSFER
191170 ? 'Cancel Transfer'
192−
193171 : route === ROUTE_NAMES.GEN_KEYFILE
194172 ? 'Keyfile'
195−
196173 : route === ROUTE_NAMES.SET_KEYS
197174 ? 'Configure Keys'
198−
199175 : route === ROUTE_NAMES.SENT_TRANSACTION
200176 ? 'Txn'
177+ : 'Bridge';
178+};
201179
202− : 'Bridge'
203− )
204−}
205−
206−const router = (route) =>
207− ROUTES[route]
180+const router = route => ROUTES[route];
208181
209−export {
210− ROUTE_NAMES,
211− router,
212− renderRoute
213−}
182+export { ROUTE_NAMES, router, renderRoute };
src/bridge/lib/state.js+5−17
@@ -1,48 +1,36 @@
1−
2−
31const invert = (key, state) => {
42 return {
53 [key]: !state[key],
6− }
4+ };
75};
86
9−
107// const invert = (state, key) => {
118// return {
129// [key]: !state[key],
1310// }
1411// };
1512
16−
1713const toTrue = key => {
1814 return {
1915 [key]: true,
20− }
16+ };
2117};
2218
2319const toFalse = key => {
2420 return {
2521 [key]: false,
26− }
22+ };
2723};
2824
2925const toNull = key => {
3026 return {
3127 [key]: false,
32− }
28+ };
3329};
3430
35−
3631// const chain = (context, key, data, ...fs) => {
3732// fs.forEach(f => f());
3833// context.setState({[k]: data});
3934// };
4035
41−
42−
43−export {
44− invert,
45− toTrue,
46− toFalse,
47− toNull,
48−}
36+export { invert, toTrue, toFalse, toNull };
src/bridge/lib/tank.js+37−31
@@ -1,6 +1,6 @@
11// /lib/tank: functions for funding transactions
22
3−import { waitForTransactionConfirm } from './txn'
3+import { waitForTransactionConfirm } from './txn';
44
55//NOTE if accessing this in a localhost configuration fails with "CORS request
66// did not succeed", you might need to visit localhost:3001 or whatever
@@ -14,28 +14,28 @@ function sendRequest(where, what) {
1414 method: 'POST',
1515 cache: 'no-cache',
1616 headers: {
17− 'Content-Type': 'application/json'
17+ 'Content-Type': 'application/json',
1818 },
19− body: JSON.stringify(what)
19+ body: JSON.stringify(what),
2020 })
21− .then(response => {
22− if (response.ok) {
23− resolve(response.json());
24− } else {
25− reject(response);
26− }
27− })
28− .catch(reject);
21+ .then(response => {
22+ if (response.ok) {
23+ resolve(response.json());
24+ } else {
25+ reject(response);
26+ }
27+ })
28+ .catch(reject);
2929 });
3030}
3131
3232const remainingTransactions = point => {
3333 if (typeof point === 'string') point = Number(point);
34− return sendRequest('/point', {point:point});
34+ return sendRequest('/point', { point: point });
3535};
3636
3737const fundTransactions = signedTxs => {
38− return sendRequest('/request', {txs:signedTxs});
38+ return sendRequest('/request', { txs: signedTxs });
3939};
4040
4141// resolves when address has at least cost balance
@@ -43,14 +43,19 @@ const fundTransactions = signedTxs => {
4343// askForFunding: callback that takes (address, requiredBalance, currentBalance)
4444// and tells the user to get that address the required balance
4545// gotFunding: optional callback that stops telling the user to go get funding
46−const ensureFundsFor = async (web3, point, address, cost, signedTxs,
47− askForFunding, gotFunding) => {
46+const ensureFundsFor = async (
47+ web3,
48+ point,
49+ address,
50+ cost,
51+ signedTxs,
52+ askForFunding,
53+ gotFunding
54+) => {
4855 let balance = await web3.eth.getBalance(address);
4956
5057 if (cost > balance) {
51−
5258 try {
53−
5459 if (point !== null) {
5560 const fundsRemaining = await remainingTransactions(point);
5661 if (fundsRemaining < signedTxs.length) {
@@ -69,17 +74,18 @@ const ensureFundsFor = async (web3, point, address, cost, signedTxs,
6974 } else {
7075 await waitForTransactionConfirm(web3, res.txHash);
7176 let newBalance = await web3.eth.getBalance(address);
72− console.log('tank: funds have confirmed', balance >= cost, balance, newBalance);
77+ console.log(
78+ 'tank: funds have confirmed',
79+ balance >= cost,
80+ balance,
81+ newBalance
82+ );
7383 return true;
7484 }
75−
7685 } catch (e) {
77−
7886 console.log('tank: funding failed', e);
7987 await waitForBalance(web3, address, cost, askForFunding, gotFunding);
80−
8188 }
82−
8389 } else {
8490 console.log('tank: already have sufficient funds');
8591 }
@@ -89,8 +95,13 @@ const ensureFundsFor = async (web3, point, address, cost, signedTxs,
8995// resolves when address has at least minBalance
9096//
9197//TODO should maybe do a "we got it" callback also, so clients can hide msg?
92−async function waitForBalance(web3, address, minBalance,
93− askForFunding, gotFunding) {
98+async function waitForBalance(
99+ web3,
100+ address,
101+ minBalance,
102+ askForFunding,
103+ gotFunding
104+) {
94105 console.log('tank: awaiting balance', address, minBalance);
95106 return new Promise((resolve, reject) => {
96107 let oldBalance = null;
@@ -98,7 +109,7 @@ async function waitForBalance(web3, address, minBalance,
98109 const balance = await web3.eth.getBalance(address);
99110 if (balance >= minBalance) {
100111 // if we ever asked for funding, retract that request now
101− if (gotFunding && (oldBalance !== null)) {
112+ if (gotFunding && oldBalance !== null) {
102113 gotFunding();
103114 }
104115 resolve();
@@ -113,9 +124,4 @@ async function waitForBalance(web3, address, minBalance,
113124 });
114125}
115126
116−export {
117− remainingTransactions,
118− fundTransactions,
119− ensureFundsFor
120−}
121−
127+export { remainingTransactions, fundTransactions, ensureFundsFor };
src/bridge/lib/transformers.js+2−3
@@ -1,6 +1,6 @@
11// import { fill } from './lib'
22
3−const prependSig = s => s.charAt(0) !== '~' ? `~${s}` : s;
3+const prependSig = s => (s.charAt(0) !== '~' ? `~${s}` : s);
44
55// const hideAllButLast = s => {
66// const ll = s[s.length -1];
@@ -10,9 +10,8 @@ const prependSig = s => s.charAt(0) !== '~' ? `~${s}` : s;
1010//
1111// const hideAll = s => fill(s.length, '•');
1212
13−
1413export {
1514 prependSig,
1615 // hideAllButLast,
1716 // hideAll,
18−}
17+};
src/bridge/lib/trezor.js+14−20
@@ -1,11 +1,9 @@
1−import TrezorConnect from 'trezor-connect'
1+import TrezorConnect from 'trezor-connect';
22
3−const TREZOR_PATH = "m/44'/60'/0'/0/x"
3+const TREZOR_PATH = "m/44'/60'/0'/0/x";
44
55const formatChainId = val =>
6− typeof val === 'number'
7− ? val
8− : parseInt(val.slice(2), 16) // assume 0x-prefixed hex string
6+ typeof val === 'number' ? val : parseInt(val.slice(2), 16); // assume 0x-prefixed hex string
97
108const trezorSignTransaction = async (txn, hdpath) => {
119 const trezorFormattedTxn = {
@@ -15,25 +13,21 @@ const trezorSignTransaction = async (txn, hdpath) => {
1513 gasLimit: txn.gasLimit.toString('hex'),
1614 gasPrice: txn.gasPrice.toString('hex'),
1715 nonce: txn.nonce.length === 0 ? '00' : txn.nonce.toString('hex'),
18− chainId: formatChainId(txn.getChainId())
19− }
16+ chainId: formatChainId(txn.getChainId()),
17+ };
2018
2119 const sig = await TrezorConnect.ethereumSignTransaction({
2220 path: hdpath,
23− transaction: trezorFormattedTxn
24− })
21+ transaction: trezorFormattedTxn,
22+ });
2523
26− const payload = sig.payload
24+ const payload = sig.payload;
2725
28− txn.v = Buffer.from(payload.v.slice(2), 'hex')
29− txn.r = Buffer.from(payload.r.slice(2), 'hex')
30− txn.s = Buffer.from(payload.s.slice(2), 'hex')
26+ txn.v = Buffer.from(payload.v.slice(2), 'hex');
27+ txn.r = Buffer.from(payload.r.slice(2), 'hex');
28+ txn.s = Buffer.from(payload.s.slice(2), 'hex');
3129
32− return txn
33−}
34−
35−export {
36− TREZOR_PATH,
37− trezorSignTransaction
38−}
30+ return txn;
31+};
3932
33+export { TREZOR_PATH, trezorSignTransaction };
src/bridge/lib/txn.js+121−123
@@ -1,16 +1,13 @@
1−import * as ob from 'urbit-ob'
2−import { Just } from 'folktale/maybe'
3−import Tx from 'ethereumjs-tx'
4−import { toWei, fromWei, toHex } from 'web3-utils'
5−
6−import { BRIDGE_ERROR } from '../lib/error'
7−import { NETWORK_NAMES } from '../lib/network'
8−import { ledgerSignTransaction } from '../lib/ledger'
9−import { trezorSignTransaction } from '../lib/trezor'
10−import {
11− WALLET_NAMES,
12− addHexPrefix
13− } from '../lib/wallet'
1+import * as ob from 'urbit-ob';
2+import { Just } from 'folktale/maybe';
3+import Tx from 'ethereumjs-tx';
4+import { toWei, fromWei, toHex } from 'web3-utils';
5+
6+import { BRIDGE_ERROR } from '../lib/error';
7+import { NETWORK_NAMES } from '../lib/network';
8+import { ledgerSignTransaction } from '../lib/ledger';
9+import { trezorSignTransaction } from '../lib/trezor';
10+import { WALLET_NAMES, addHexPrefix } from '../lib/wallet';
1411
1512const TXN_PURPOSE = {
1613 SET_MANAGEMENT_PROXY: Symbol('SET_MANAGEMENT_PROXY'),
@@ -20,30 +17,29 @@ const TXN_PURPOSE = {
2017 ISSUE_CHILD: Symbol('ISSUE_CHILD'),
2118 SET_KEYS: Symbol('SET_KEYS'),
2219 TRANSFER: Symbol('TRANSFER'),
23− CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER')
24−}
25−
26−const renderTxnPurpose = (purpose) =>
27− purpose === TXN_PURPOSE.SET_MANAGEMENT_PROXY
28− ? 'set this management proxy'
29− : purpose === TXN_PURPOSE.SET_SPAWN_PROXY
30− ? 'set this spawn proxy'
31− : purpose === TXN_PURPOSE.SET_TRANSFER_PROXY
32− ? 'set this transfer proxy'
33− : purpose === TXN_PURPOSE.CREATE_GALAXY
34− ? 'create this galaxy'
35− : purpose === TXN_PURPOSE.ISSUE_CHILD
36− ? 'issue this point'
37− : purpose === TXN_PURPOSE.SET_KEYS
38− ? 'set these network keys'
39− : purpose === TXN_PURPOSE.TRANSFER
40− ? 'transfer this point'
41− : purpose === TXN_PURPOSE.CANCEL_TRANSFER
42− ? 'cancel this transfer'
43− : 'perform this transaction'
20+ CANCEL_TRANSFER: Symbol('CANCEL_TRANSFER'),
21+};
22+
23+const renderTxnPurpose = purpose =>
24+ purpose === TXN_PURPOSE.SET_MANAGEMENT_PROXY
25+ ? 'set this management proxy'
26+ : purpose === TXN_PURPOSE.SET_SPAWN_PROXY
27+ ? 'set this spawn proxy'
28+ : purpose === TXN_PURPOSE.SET_TRANSFER_PROXY
29+ ? 'set this transfer proxy'
30+ : purpose === TXN_PURPOSE.CREATE_GALAXY
31+ ? 'create this galaxy'
32+ : purpose === TXN_PURPOSE.ISSUE_CHILD
33+ ? 'issue this point'
34+ : purpose === TXN_PURPOSE.SET_KEYS
35+ ? 'set these network keys'
36+ : purpose === TXN_PURPOSE.TRANSFER
37+ ? 'transfer this point'
38+ : purpose === TXN_PURPOSE.CANCEL_TRANSFER
39+ ? 'cancel this transfer'
40+ : 'perform this transaction';
4441
4542const signTransaction = async config => {
46−
4743 let {
4844 wallet,
4945 walletType,
@@ -54,15 +50,15 @@ const signTransaction = async config => {
5450 nonce,
5551 chainId,
5652 gasPrice,
57− gasLimit
58− } = config
53+ gasLimit,
54+ } = config;
5955
60− nonce = toHex(nonce)
61− chainId = toHex(chainId)
62− gasPrice = toHex(toWei(gasPrice, 'gwei'))
63− gasLimit = toHex(gasLimit)
56+ nonce = toHex(nonce);
57+ chainId = toHex(chainId);
58+ gasPrice = toHex(toWei(gasPrice, 'gwei'));
59+ gasLimit = toHex(gasLimit);
6460
65− const txParams = { nonce, chainId, gasPrice, gasLimit }
61+ const txParams = { nonce, chainId, gasPrice, gasLimit };
6662
6763 // NB (jtobin)
6864 //
@@ -90,102 +86,104 @@ const signTransaction = async config => {
9086 const eip155Params = {
9187 r: '0x00',
9288 s: '0x00',
93− v: chainId
94− }
89+ v: chainId,
90+ };
9591
9692 const defaultEip155Networks = [
9793 NETWORK_NAMES.MAINNET,
9894 NETWORK_NAMES.ROPSTEN,
99− NETWORK_NAMES.OFFLINE
100− ]
95+ NETWORK_NAMES.OFFLINE,
96+ ];
10197
10298 const needEip155Params =
10399 walletType === WALLET_NAMES.LEDGER &&
104− defaultEip155Networks.includes(networkType)
100+ defaultEip155Networks.includes(networkType);
105101
106− const signingParams =
107− needEip155Params
102+ const signingParams = needEip155Params
108103 ? Object.assign(txParams, eip155Params)
109− : txParams
104+ : txParams;
110105
111106 const wal = wallet.matchWith({
112− Just: (w) => w.value,
113− Nothing: () => { throw BRIDGE_ERROR.MISSING_WALLET }
114− })
107+ Just: w => w.value,
108+ Nothing: () => {
109+ throw BRIDGE_ERROR.MISSING_WALLET;
110+ },
111+ });
115112
116− const sec = wal.privateKey
113+ const sec = wal.privateKey;
117114
118115 const utx = txn.matchWith({
119− Just: (tx) =>
120− Object.assign(tx.value, signingParams),
116+ Just: tx => Object.assign(tx.value, signingParams),
121117 Nothing: () => {
122− throw BRIDGE_ERROR.MISSING_TXN
123− }
124− })
118+ throw BRIDGE_ERROR.MISSING_TXN;
119+ },
120+ });
125121
126− const stx = new Tx(utx)
122+ const stx = new Tx(utx);
127123
128124 if (walletType === WALLET_NAMES.LEDGER) {
129− await ledgerSignTransaction(stx, walletHdPath)
125+ await ledgerSignTransaction(stx, walletHdPath);
130126 } else if (walletType === WALLET_NAMES.TREZOR) {
131− await trezorSignTransaction(stx, walletHdPath)
127+ await trezorSignTransaction(stx, walletHdPath);
132128 } else {
133− stx.sign(sec)
129+ stx.sign(sec);
134130 }
135131
136132 setStx(Just(stx));
137133 return stx;
138−}
134+};
139135
140136const sendSignedTransaction = (web3, stx, doubtNonceError, confirmationCb) => {
141137 const txn = stx.matchWith({
142− Just: (tx) => tx.value,
138+ Just: tx => tx.value,
143139 Nothing: () => {
144− throw BRIDGE_ERROR.MISSING_TXN
145− }
140+ throw BRIDGE_ERROR.MISSING_TXN;
141+ },
146142 });
147143
148144 const rawTx = addHexPrefix(txn.serialize().toString('hex'));
149145
150146 return new Promise(async (resolve, reject) => {
151− web3.eth.sendSignedTransaction(rawTx)
152− .on('transactionHash', hash => {
153− resolve(hash);
154− })
155− //TODO do we also reach this if network is slow? web3 only tries a set
156− // amount of times... should we instead do waitForTransactionConfirm
157− // in on-transactionHash? we don't care (much) about additional
158− // confirms anyway.
159− .on('confirmation', (confirmationNum, txn) => {
160− confirmationCb(txn.transactionHash, confirmationNum + 1);
161− resolve(txn.transactionHash);
162− })
163− .on('error', err => {
164− // if there's a nonce error, but we used the gas tank, it's likely
165− // that it's because the tank already submitted our transaction.
166− // we just wait for first confirmation here.
167− console.log('sent and got error', err);
168− if (doubtNonceError &&
169− (err.message || '').includes("the tx doesn't have the correct nonce.")) {
170− console.log('nonce error, likely from gas tank submission, ignoring');
171− const txHash = web3.utils.keccak256(rawTx);
172− //TODO can we do does-exists check first?
173− //TODO max wait time before assume fail?
174− waitForTransactionConfirm(web3, txHash)
175− .then(res => {
176− if (res) {
177− resolve(txHash);
178− confirmationCb(txHash, 1);
179− } else {
180− reject('Unexpected tx failure');
181− }
182− });
183− } else {
184− reject(err.message);
185− }
186− });
147+ web3.eth
148+ .sendSignedTransaction(rawTx)
149+ .on('transactionHash', hash => {
150+ resolve(hash);
151+ })
152+ //TODO do we also reach this if network is slow? web3 only tries a set
153+ // amount of times... should we instead do waitForTransactionConfirm
154+ // in on-transactionHash? we don't care (much) about additional
155+ // confirms anyway.
156+ .on('confirmation', (confirmationNum, txn) => {
157+ confirmationCb(txn.transactionHash, confirmationNum + 1);
158+ resolve(txn.transactionHash);
159+ })
160+ .on('error', err => {
161+ // if there's a nonce error, but we used the gas tank, it's likely
162+ // that it's because the tank already submitted our transaction.
163+ // we just wait for first confirmation here.
164+ console.log('sent and got error', err);
165+ if (
166+ doubtNonceError &&
167+ (err.message || '').includes("the tx doesn't have the correct nonce.")
168+ ) {
169+ console.log('nonce error, likely from gas tank submission, ignoring');
170+ const txHash = web3.utils.keccak256(rawTx);
171+ //TODO can we do does-exists check first?
172+ //TODO max wait time before assume fail?
173+ waitForTransactionConfirm(web3, txHash).then(res => {
174+ if (res) {
175+ resolve(txHash);
176+ confirmationCb(txHash, 1);
177+ } else {
178+ reject('Unexpected tx failure');
179+ }
180+ });
181+ } else {
182+ reject(err.message);
183+ }
184+ });
187185 });
188−}
186+};
189187
190188// returns a Promise<bool>, where the bool indicates tx success/failure
191189const waitForTransactionConfirm = (web3, txHash) => {
@@ -194,49 +192,49 @@ const waitForTransactionConfirm = (web3, txHash) => {
194192 console.log('checking for confirm', txHash);
195193 const receipt = await web3.eth.getTransactionReceipt(txHash);
196194 console.log('tried, got', receipt);
197− let confirmed = (receipt !== null);
195+ let confirmed = receipt !== null;
198196 if (confirmed) resolve(receipt.status === true);
199197 else setTimeout(checkForConfirm, 13000);
200− }
198+ };
201199 checkForConfirm();
202200 });
203−}
201+};
204202
205203const isTransactionConfirmed = async (web3, txHash) => {
206204 const receipt = await web3.eth.getTransactionReceipt(txHash);
207205 console.log('got confirm state', receipt !== null, receipt.confirmations);
208− return (receipt !== null);
209−}
206+ return receipt !== null;
207+};
210208
211−const hexify = val => addHexPrefix(val.toString('hex'))
209+const hexify = val => addHexPrefix(val.toString('hex'));
212210
213211const renderSignedTx = stx => ({
214212 messageHash: hexify(stx.hash()),
215213 v: hexify(stx.v),
216214 s: hexify(stx.s),
217215 r: hexify(stx.r),
218− rawTransaction: hexify(stx.serialize())
219−})
216+ rawTransaction: hexify(stx.serialize()),
217+});
220218
221219const getTxnInfo = async (web3, addr) => {
222− let nonce = await web3.eth.getTransactionCount(addr)
223− let chainId = await web3.eth.net.getId()
224− let gasPrice = await web3.eth.getGasPrice()
220+ let nonce = await web3.eth.getTransactionCount(addr);
221+ let chainId = await web3.eth.net.getId();
222+ let gasPrice = await web3.eth.getGasPrice();
225223 return {
226224 nonce: nonce,
227225 chainId: chainId,
228− gasPrice: fromWei(gasPrice, 'gwei')
229− }
230−}
226+ gasPrice: fromWei(gasPrice, 'gwei'),
227+ };
228+};
231229
232230const canDecodePatp = p => {
233231 try {
234− ob.patp2dec(p)
235− return true
232+ ob.patp2dec(p);
233+ return true;
236234 } catch (_) {
237− return false
235+ return false;
238236 }
239−}
237+};
240238
241239export {
242240 signTransaction,
@@ -252,4 +250,4 @@ export {
252250 toWei,
253251 fromWei,
254252 canDecodePatp,
255−}
253+};
src/bridge/lib/validators.js+100−91
@@ -1,9 +1,9 @@
11import * as bip39 from 'bip39';
22import * as ob from 'urbit-ob';
33
4−import { isValidAddress } from './wallet'
4+import { isValidAddress } from './wallet';
55
6−const hexRegExp = /[0-9A-Fa-f]{64}/g
6+const hexRegExp = /[0-9A-Fa-f]{64}/g;
77
88// // Note that this validator also transforms data
99// const validateBase64 = ({ data, pass, error }) => {
@@ -30,109 +30,118 @@ const hexRegExp = /[0-9A-Fa-f]{64}/g
3030// };
3131
3232// Validates a bip39 mnemonic
33−const validateMnemonic = m => simpleValidatorWrapper({
34− prevMessage: m,
35− validator: d => bip39.validateMnemonic(d),
36− errorMessage: 'This is not a valid mnemonic.'
37−});
33+const validateMnemonic = m =>
34+ simpleValidatorWrapper({
35+ prevMessage: m,
36+ validator: d => bip39.validateMnemonic(d),
37+ errorMessage: 'This is not a valid mnemonic.',
38+ });
3839
3940// Checks an empty field
40−const validateNotEmpty = m => simpleValidatorWrapper({
41− prevMessage: m,
42− validator: d => d.length > 1,
43− errorMessage: 'This field is required.'
44−});
41+const validateNotEmpty = m =>
42+ simpleValidatorWrapper({
43+ prevMessage: m,
44+ validator: d => d.length > 1,
45+ errorMessage: 'This field is required.',
46+ });
4547
4648// Checks if a patp is a valid galaxy
47−const validateGalaxy = m => simpleValidatorWrapper({
48− prevMessage: m,
49− validator: d => {
50− let point
51− try {
52− point = parseInt(ob.patp2dec(d), 10)
53− return point >= 0 && point < 256
54− } catch(e) {
55− return false
56− }
57− },
58− errorMessage: 'This is not a valid galaxy'
59−});
60−
61−const validatePoint = m => simpleValidatorWrapper({
62− prevMessage: m,
63− validator: d => {
64− try {
65− return ob.isValidPatp(d)
66− } catch(e) {
67− return false
68− }
69− },
70− errorMessage: 'This is not a valid point'
71−});
72−
73−const validateTicket = m => simpleValidatorWrapper({
74− prevMessage: m,
75− validator: d => {
76− try {
77− return ob.isValidPatq(d)
78− } catch(e) {
79− return false
80− }
81− },
82− errorMessage: 'This is not a valid ticket'
83−});
84−
85−const validateShard = m => simpleValidatorWrapper({
86− prevMessage: m,
87− validator: d => {
88− try {
89− return ob.isValidPatq(d)
90− } catch(e) {
91− return false
92− }
93− },
94− errorMessage: 'This is not a valid shard'
95−});
96−
97−const validateLength = (m, l) => simpleValidatorWrapper({
98− prevMessage: m,
99− validator: d => d.length === l,
100− errorMessage: 'This is of an invalid length'
101−});
102−
103−const validateNetworkKey = m => simpleValidatorWrapper({
104− prevMessage: m,
105− validator: d => hexRegExp.test(d),
106− errorMessage: 'This is not a valid network key'
107−});
108−
109−const validateNetworkSeed = m => simpleValidatorWrapper({
110− prevMessage: m,
111− validator: d => hexRegExp.test(d),
112− errorMessage: 'This is not a valid network seed'
113−});
49+const validateGalaxy = m =>
50+ simpleValidatorWrapper({
51+ prevMessage: m,
52+ validator: d => {
53+ let point;
54+ try {
55+ point = parseInt(ob.patp2dec(d), 10);
56+ return point >= 0 && point < 256;
57+ } catch (e) {
58+ return false;
59+ }
60+ },
61+ errorMessage: 'This is not a valid galaxy',
62+ });
63+
64+const validatePoint = m =>
65+ simpleValidatorWrapper({
66+ prevMessage: m,
67+ validator: d => {
68+ try {
69+ return ob.isValidPatp(d);
70+ } catch (e) {
71+ return false;
72+ }
73+ },
74+ errorMessage: 'This is not a valid point',
75+ });
76+
77+const validateTicket = m =>
78+ simpleValidatorWrapper({
79+ prevMessage: m,
80+ validator: d => {
81+ try {
82+ return ob.isValidPatq(d);
83+ } catch (e) {
84+ return false;
85+ }
86+ },
87+ errorMessage: 'This is not a valid ticket',
88+ });
89+
90+const validateShard = m =>
91+ simpleValidatorWrapper({
92+ prevMessage: m,
93+ validator: d => {
94+ try {
95+ return ob.isValidPatq(d);
96+ } catch (e) {
97+ return false;
98+ }
99+ },
100+ errorMessage: 'This is not a valid shard',
101+ });
102+
103+const validateLength = (m, l) =>
104+ simpleValidatorWrapper({
105+ prevMessage: m,
106+ validator: d => d.length === l,
107+ errorMessage: 'This is of an invalid length',
108+ });
109+
110+const validateNetworkKey = m =>
111+ simpleValidatorWrapper({
112+ prevMessage: m,
113+ validator: d => hexRegExp.test(d),
114+ errorMessage: 'This is not a valid network key',
115+ });
116+
117+const validateNetworkSeed = m =>
118+ simpleValidatorWrapper({
119+ prevMessage: m,
120+ validator: d => hexRegExp.test(d),
121+ errorMessage: 'This is not a valid network seed',
122+ });
114123
115124// Checks if a string is a valid ethereum address
116−const validateEthereumAddress = m => simpleValidatorWrapper({
117− prevMessage: m,
118− validator: d => isValidAddress(d),
119− errorMessage: 'This is not a valid Ethereum address'
120−});
125+const validateEthereumAddress = m =>
126+ simpleValidatorWrapper({
127+ prevMessage: m,
128+ validator: d => isValidAddress(d),
129+ errorMessage: 'This is not a valid Ethereum address',
130+ });
121131
122132// Wraps single validation functions in a controlled and predictable way.
123133const simpleValidatorWrapper = config => {
124−
125134 // If a previous validation has already failed, skip this validation and
126135 // return the prev message to the next stage in the validation function chain.
127136 // Failed validations should drop all the way down the chain and drop out of
128137 // the output.
129− if (config.prevMessage.pass !== true) return config.prevMessage
138+ if (config.prevMessage.pass !== true) return config.prevMessage;
130139
131140 // Run the validator and return the result.
132141 return config.validator(config.prevMessage.data)
133142 ? newMessage(config.prevMessage.data, true, '')
134− : newMessage(config.prevMessage.data, false, config.errorMessage)
135−}
143+ : newMessage(config.prevMessage.data, false, config.errorMessage);
144+};
136145
137146// Validation message
138147// {
@@ -148,7 +157,7 @@ const newMessage = (data, pass, error) => ({
148157 pass,
149158 // If data has failed a validator, the error message goes here.
150159 error,
151−})
160+});
152161
153162export {
154163 // validateBase64,
@@ -163,5 +172,5 @@ export {
163172 validateShard,
164173 validateLength,
165174 validateNetworkKey,
166− validateNetworkSeed
167−}
175+ validateNetworkSeed,
176+};
src/bridge/lib/wallet.js+82−84
@@ -1,17 +1,18 @@
1−import * as bip32 from 'bip32'
2−import * as bip39 from 'bip39'
3−import keccak from 'keccak'
4−import * as lodash from 'lodash'
5−import Maybe from 'folktale/maybe'
6−import * as secp256k1 from 'secp256k1'
7−import * as ob from 'urbit-ob'
8−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
9−import { isAddress } from 'web3-utils'
10−
11−const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0"
12−
13−const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000'
14−const CURVE_ZERO_ADDR = "0x0000000000000000000000000000000000000000000000000000000000000000"
1+import * as bip32 from 'bip32';
2+import * as bip39 from 'bip39';
3+import keccak from 'keccak';
4+import * as lodash from 'lodash';
5+import Maybe from 'folktale/maybe';
6+import * as secp256k1 from 'secp256k1';
7+import * as ob from 'urbit-ob';
8+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
9+import { isAddress } from 'web3-utils';
10+
11+const DEFAULT_HD_PATH = "m/44'/60'/0'/0/0";
12+
13+const ETH_ZERO_ADDR = '0x0000000000000000000000000000000000000000';
14+const CURVE_ZERO_ADDR =
15+ '0x0000000000000000000000000000000000000000000000000000000000000000';
1516
1617const WALLET_NAMES = {
1718 MNEMONIC: Symbol('MNEMONIC'),
@@ -20,120 +21,117 @@ const WALLET_NAMES = {
2021 LEDGER: Symbol('LEDGER'),
2122 TREZOR: Symbol('TREZOR'),
2223 PRIVATE_KEY: Symbol('PRIVATE_KEY'),
23− KEYSTORE: Symbol('KEYSTORE')
24−}
24+ KEYSTORE: Symbol('KEYSTORE'),
25+};
2526
2627function EthereumWallet(privateKey) {
27− this.privateKey = privateKey
28− this.publicKey = secp256k1.publicKeyCreate(this.privateKey)
29− const pub = this.publicKey.toString('hex')
30− this.address = addressFromSecp256k1Public(pub)
28+ this.privateKey = privateKey;
29+ this.publicKey = secp256k1.publicKeyCreate(this.privateKey);
30+ const pub = this.publicKey.toString('hex');
31+ this.address = addressFromSecp256k1Public(pub);
3132}
3233
33−const renderWalletType = (wallet) =>
34− wallet === WALLET_NAMES.MNEMONIC
35− ? 'Mnemonic'
36− : wallet === WALLET_NAMES.TICKET
37− ? 'Ticket'
38− : wallet === WALLET_NAMES.SHARDS
39− ? 'Ticket Shards'
40− : wallet === WALLET_NAMES.LEDGER
41− ? 'Ledger'
42− : wallet === WALLET_NAMES.TREZOR
43− ? 'Trezor'
44− : wallet === WALLET_NAMES.PRIVATE_KEY
45− ? 'Private Key'
46− : wallet === WALLET_NAMES.KEYSTORE
47− ? 'Keystore File'
48− : 'Wallet'
34+const renderWalletType = wallet =>
35+ wallet === WALLET_NAMES.MNEMONIC
36+ ? 'Mnemonic'
37+ : wallet === WALLET_NAMES.TICKET
38+ ? 'Ticket'
39+ : wallet === WALLET_NAMES.SHARDS
40+ ? 'Ticket Shards'
41+ : wallet === WALLET_NAMES.LEDGER
42+ ? 'Ledger'
43+ : wallet === WALLET_NAMES.TREZOR
44+ ? 'Trezor'
45+ : wallet === WALLET_NAMES.PRIVATE_KEY
46+ ? 'Private Key'
47+ : wallet === WALLET_NAMES.KEYSTORE
48+ ? 'Keystore File'
49+ : 'Wallet';
4950
5051const addressFromSecp256k1Public = pub => {
51− const compressed = false
52+ const compressed = false;
5253 const uncompressed = secp256k1.publicKeyConvert(
5354 Buffer.from(pub, 'hex'),
5455 compressed
55− )
56− const chopped = uncompressed.slice(1) // chop parity byte
57− const hashed = keccak256(chopped)
58− const addr = addHexPrefix(hashed.slice(-20).toString('hex'))
59− return toChecksumAddress(addr)
60−}
56+ );
57+ const chopped = uncompressed.slice(1); // chop parity byte
58+ const hashed = keccak256(chopped);
59+ const addr = addHexPrefix(hashed.slice(-20).toString('hex'));
60+ return toChecksumAddress(addr);
61+};
6162
62−const addHexPrefix = hex =>
63− hex.slice(0, 2) === '0x'
64− ? hex
65− : '0x' + hex
63+const addHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex : '0x' + hex);
6664
67−const stripHexPrefix = hex =>
68− hex.slice(0, 2) === '0x'
69− ? hex.slice(2)
70− : hex
65+const stripHexPrefix = hex => (hex.slice(0, 2) === '0x' ? hex.slice(2) : hex);
7166
7267const keccak256 = str =>
73− keccak('keccak256').update(str).digest()
68+ keccak('keccak256')
69+ .update(str)
70+ .digest();
7471
75−const isValidAddress = a => (('0x0' === a) || isAddress(a))
72+const isValidAddress = a => '0x0' === a || isAddress(a);
7673
77−const toChecksumAddress = (address) => {
78− const addr = stripHexPrefix(address).toLowerCase()
79− const hash = keccak256(addr).toString('hex')
74+const toChecksumAddress = address => {
75+ const addr = stripHexPrefix(address).toLowerCase();
76+ const hash = keccak256(addr).toString('hex');
8077
81− return lodash.reduce(addr, (acc, char, idx) =>
82− parseInt(hash[idx], 16) >= 8
83− ? acc + char.toUpperCase()
84− : acc + char,
85− '0x')
86−}
78+ return lodash.reduce(
79+ addr,
80+ (acc, char, idx) =>
81+ parseInt(hash[idx], 16) >= 8 ? acc + char.toUpperCase() : acc + char,
82+ '0x'
83+ );
84+};
8785
8886const eqAddr = (addr0, addr1) =>
89− toChecksumAddress(addr0) === toChecksumAddress(addr1)
87+ toChecksumAddress(addr0) === toChecksumAddress(addr1);
9088
9189const urbitWalletFromTicket = async (ticket, point, passphrase) => {
92− if (typeof point === 'string') { //TODO why not in kg?
90+ if (typeof point === 'string') {
91+ //TODO why not in kg?
9392 point = ob.patp2dec(point);
9493 }
9594 let uhdw = await kg.generateWallet({
9695 ticket: ticket,
9796 ship: point,
98− passphrase: passphrase
97+ passphrase: passphrase,
9998 });
10099 return uhdw;
101−}
102−
100+};
103101
104102const ownershipWalletFromTicket = async (ticket, point, passphrase) => {
105− if (typeof point === 'string') { //TODO why not in kg?
103+ if (typeof point === 'string') {
104+ //TODO why not in kg?
106105 point = ob.patp2dec(point);
107106 }
108107 return await kg.generateOwnershipWallet({
109108 ticket,
110109 ship: point,
111− passphrase
112− })
113−}
110+ passphrase,
111+ });
112+};
114113
115114const walletFromMnemonic = (mnemonic, hdpath, passphrase) => {
116− const seed =
117− bip39.validateMnemonic(mnemonic)
115+ const seed = bip39.validateMnemonic(mnemonic)
118116 ? Maybe.Just(bip39.mnemonicToSeed(mnemonic, passphrase))
119− : Maybe.Nothing()
117+ : Maybe.Nothing();
120118
121119 const toWallet = (sd, path) => {
122− let wal
120+ let wal;
123121 try {
124− const hd = bip32.fromSeed(sd)
122+ const hd = bip32.fromSeed(sd);
125123 wal = hd.derivePath(path);
126124 wal.address = addressFromSecp256k1Public(wal.publicKey);
127125 wal = Maybe.Just(wal);
128126 } catch (_) {
129− wal = Maybe.Nothing()
127+ wal = Maybe.Nothing();
130128 }
131− return wal
132− }
129+ return wal;
130+ };
133131
134− const wallet = seed.chain(sd => toWallet(sd, hdpath))
135− return wallet
136−}
132+ const wallet = seed.chain(sd => toWallet(sd, hdpath));
133+ return wallet;
134+};
137135
138136export {
139137 DEFAULT_HD_PATH,
@@ -149,5 +147,5 @@ export {
149147 ownershipWalletFromTicket,
150148 walletFromMnemonic,
151149 addHexPrefix,
152− EthereumWallet
153−}
150+ EthereumWallet,
151+};
src/bridge/views/AcceptTransfer.js+59−50
@@ -1,18 +1,23 @@
1−import { Just } from 'folktale/maybe'
2−import React from 'react'
3−import { Row, Col, H1, P } from '../components/Base'
4−import { InnerLabel, AddressInput, ShowBlockie, Anchor } from '../components/Base'
5−import * as azimuth from 'azimuth-js'
6−import * as ob from 'urbit-ob'
7−import * as need from '../lib/need'
8−
9−import StatelessTransaction from '../components/StatelessTransaction'
10−import { NETWORK_NAMES } from '../lib/network'
11−import { isValidAddress } from '../lib/wallet'
1+import { Just } from 'folktale/maybe';
2+import React from 'react';
3+import { Row, Col, H1, P } from '../components/Base';
4+import {
5+ InnerLabel,
6+ AddressInput,
7+ ShowBlockie,
8+ Anchor,
9+} from '../components/Base';
10+import * as azimuth from 'azimuth-js';
11+import * as ob from 'urbit-ob';
12+import * as need from '../lib/need';
13+
14+import StatelessTransaction from '../components/StatelessTransaction';
15+import { NETWORK_NAMES } from '../lib/network';
16+import { isValidAddress } from '../lib/wallet';
1217
1318class AcceptTransfer extends React.Component {
1419 constructor(props) {
15− super(props)
20+ super(props);
1621
1722 const receivingAddress = need.address(props);
1823
@@ -21,91 +26,95 @@ class AcceptTransfer extends React.Component {
2126 this.state = {
2227 receivingAddress: receivingAddress,
2328 incomingPoint: incomingPoint,
24− }
29+ };
2530
26− this.handleAddressInput = this.handleAddressInput.bind(this)
27− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
31+ this.handleAddressInput = this.handleAddressInput.bind(this);
32+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
2833 this.statelessRef = React.createRef();
2934 }
3035
3136 handleAddressInput(proxyAddress) {
32− this.setState({ proxyAddress })
33− this.statelessRef.current.clearTxn()
37+ this.setState({ proxyAddress });
38+ this.statelessRef.current.clearTxn();
3439 }
3540
3641 createUnsignedTxn() {
37− const { state, props } = this
42+ const { state, props } = this;
3843
3944 const validContracts = need.contracts(props);
4045
4146 const validPoint = need.pointCursor(props); //TODO state.incomingPoint ?
4247
43− return Just(azimuth.ecliptic.transferPoint(
44− validContracts,
45− validPoint,
46− state.receivingAddress,
47− true
48− ))
48+ return Just(
49+ azimuth.ecliptic.transferPoint(
50+ validContracts,
51+ validPoint,
52+ state.receivingAddress,
53+ true
54+ )
55+ );
4956 }
5057
5158 render() {
52− const { state, props } = this
53− const validAddress = isValidAddress(state.receivingAddress)
54− const canGenerate = validAddress === true
59+ const { state, props } = this;
60+ const validAddress = isValidAddress(state.receivingAddress);
61+ const canGenerate = validAddress === true;
5562
5663 const esvisible =
57− props.networkType === NETWORK_NAMES.ROPSTEN ||
58− props.networkType === NETWORK_NAMES.MAINNET
64+ props.networkType === NETWORK_NAMES.ROPSTEN ||
65+ props.networkType === NETWORK_NAMES.MAINNET;
5966
6067 const esdomain =
61− props.networkType === NETWORK_NAMES.ROPSTEN
62− ? "ropsten.etherscan.io"
63− : "etherscan.io"
68+ props.networkType === NETWORK_NAMES.ROPSTEN
69+ ? 'ropsten.etherscan.io'
70+ : 'etherscan.io';
6471
6572 return (
6673 <Row>
6774 <Col>
6875 <H1>
69− { 'Accept Transfer of '} <code>{ ` ${ob.patp(state.incomingPoint)} ` }</code>
76+ {'Accept Transfer of '}{' '}
77+ <code>{` ${ob.patp(state.incomingPoint)} `}</code>
7078 </H1>
7179
7280 <P>
73− {
74− "By default, the recipient is the address you're logged in " +
75− "as. But you can transfer to any address you like."
76− }
81+ {"By default, the recipient is the address you're logged in " +
82+ 'as. But you can transfer to any address you like.'}
7783 </P>
7884
7985 <AddressInput
80− className='mono mt-8'
81− prop-size='lg'
82− prop-format='innerLabel'
83− value={ state.receivingAddress }
84− onChange={ v => this.handleAddressInput(v) }>
85− <InnerLabel>{ 'Receiving Address' }</InnerLabel>
86+ className="mono mt-8"
87+ prop-size="lg"
88+ prop-format="innerLabel"
89+ value={state.receivingAddress}
90+ onChange={v => this.handleAddressInput(v)}>
91+ <InnerLabel>{'Receiving Address'}</InnerLabel>
8692 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
8793 </AddressInput>
8894
8995 <Anchor
9096 className={'mt-1 sm'}
91− prop-size='sm'
92− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
97+ prop-size="sm"
98+ prop-disabled={
99+ !isValidAddress(state.receivingAddress) || !esvisible
100+ }
93101 target={'_blank'}
94102 href={`https://${esdomain}/address/${state.receivingAddress}`}>
95− {'View on Etherscan ↗'}
103+ {'View on Etherscan ↗'}
96104 </Anchor>
97105
98106 <StatelessTransaction
99107 // Upper scope
100108 {...props}
101109 // Other
102− canGenerate={ canGenerate }
110+ canGenerate={canGenerate}
103111 createUnsignedTxn={this.createUnsignedTxn}
104− ref={ this.statelessRef } />
112+ ref={this.statelessRef}
113+ />
105114 </Col>
106115 </Row>
107− )
116+ );
108117 }
109118}
110119
111−export default AcceptTransfer
120+export default AcceptTransfer;
src/bridge/views/CancelTransfer.js+35−37
@@ -1,29 +1,29 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import { Row, Col, H1, P } from '../components/Base'
4−import * as azimuth from 'azimuth-js'
5−import * as ob from 'urbit-ob'
6−import * as need from '../lib/need'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import { Row, Col, H1, P } from '../components/Base';
4+import * as azimuth from 'azimuth-js';
5+import * as ob from 'urbit-ob';
6+import * as need from '../lib/need';
77
8−import StatelessTransaction from '../components/StatelessTransaction'
9−import { ETH_ZERO_ADDR } from '../lib/wallet'
8+import StatelessTransaction from '../components/StatelessTransaction';
9+import { ETH_ZERO_ADDR } from '../lib/wallet';
1010
1111class CancelTransfer extends React.Component {
1212 constructor(props) {
13− super(props)
13+ super(props);
1414
1515 const pointInTransfer = need.pointCursor(props);
1616
1717 this.state = {
1818 proxyAddress: '',
1919 pointInTransfer: pointInTransfer,
20− }
20+ };
2121
22− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
22+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
2323 }
2424
2525 createUnsignedTxn() {
26− const { props } = this
26+ const { props } = this;
2727
2828 const validContracts = need.contracts(props);
2929
@@ -33,46 +33,44 @@ class CancelTransfer extends React.Component {
3333 validContracts,
3434 validPoint,
3535 ETH_ZERO_ADDR
36− )
36+ );
3737
38− return Maybe.Just(txn)
38+ return Maybe.Just(txn);
3939 }
4040
4141 render() {
42− const { props, state } = this
42+ const { props, state } = this;
4343
44− const online = Maybe.Just.hasInstance(props.web3)
44+ const online = Maybe.Just.hasInstance(props.web3);
4545
4646 const proxy = online
4747 ? props.pointCache[state.pointInTransfer].transferProxy
48− : 'any outgoing addresses'
48+ : 'any outgoing addresses';
4949
5050 // const canGenerate = validAddress === true
5151
52− const canGenerate = true
52+ const canGenerate = true;
5353
5454 return (
55− <Row>
56− <Col>
57− <H1>
58− { 'Cancel Transfer of '} <code>{ ` ${ob.patp(state.pointInTransfer)} ` }</code>
59− </H1>
60−
61− <P>
62− {
63− `This action will cancel the transfer to ${proxy}.`
64− }
65− </P>
66− <StatelessTransaction
67− // Upper scope
68− {...props}
69− // Other
70− canGenerate={ canGenerate }
71− createUnsignedTxn={this.createUnsignedTxn} />
55+ <Row>
56+ <Col>
57+ <H1>
58+ {'Cancel Transfer of '}{' '}
59+ <code>{` ${ob.patp(state.pointInTransfer)} `}</code>
60+ </H1>
61+
62+ <P>{`This action will cancel the transfer to ${proxy}.`}</P>
63+ <StatelessTransaction
64+ // Upper scope
65+ {...props}
66+ // Other
67+ canGenerate={canGenerate}
68+ createUnsignedTxn={this.createUnsignedTxn}
69+ />
7270 </Col>
7371 </Row>
74− )
72+ );
7573 }
7674}
7775
78−export default CancelTransfer
76+export default CancelTransfer;
src/bridge/views/CreateGalaxy.js+96−106
@@ -1,45 +1,42 @@
1−import React from 'react'
2−import Maybe from 'folktale/maybe'
3−import * as azimuth from 'azimuth-js'
4−import * as ob from 'urbit-ob'
5−import * as need from '../lib/need'
6−
7−import { Button, } from '../components/Base'
8−import { Row, Col, H1, P, Anchor } from '../components/Base'
9−import { InnerLabel, GalaxyInput, AddressInput, ValidatedSigil, ShowBlockie } from '../components/Base'
10−import StatelessTransaction from '../components/StatelessTransaction'
11−
12−import { NETWORK_NAMES } from '../lib/network'
13−import { canDecodePatp } from '../lib/txn'
14−
1+import React from 'react';
2+import Maybe from 'folktale/maybe';
3+import * as azimuth from 'azimuth-js';
4+import * as ob from 'urbit-ob';
5+import * as need from '../lib/need';
6+
7+import { Button } from '../components/Base';
8+import { Row, Col, H1, P, Anchor } from '../components/Base';
159import {
16− ETH_ZERO_ADDR,
17− isValidAddress,
18− eqAddr
19−} from '../lib/wallet'
10+ InnerLabel,
11+ GalaxyInput,
12+ AddressInput,
13+ ValidatedSigil,
14+ ShowBlockie,
15+} from '../components/Base';
16+import StatelessTransaction from '../components/StatelessTransaction';
2017
21−import {
22− isValidGalaxy
23−} from '../lib/lib'
18+import { NETWORK_NAMES } from '../lib/network';
19+import { canDecodePatp } from '../lib/txn';
20+
21+import { ETH_ZERO_ADDR, isValidAddress, eqAddr } from '../lib/wallet';
2422
23+import { isValidGalaxy } from '../lib/lib';
2524
2625const buttonTriState = status => {
27− if (status === null) return 'blue'
28− if (status === false) return 'yellow'
29− if (status === true) return 'green'
30−}
26+ if (status === null) return 'blue';
27+ if (status === false) return 'yellow';
28+ if (status === true) return 'green';
29+};
3130
3231const buttonTriStateText = status => {
33− if (status === null) return 'Confirm Galaxy Availablility'
34− if (status === false) return 'Galaxy is Not Available'
35− if (status === true) return 'Galaxy is Available'
36−}
37−
38−
32+ if (status === null) return 'Confirm Galaxy Availablility';
33+ if (status === false) return 'Galaxy is Not Available';
34+ if (status === true) return 'Galaxy is Available';
35+};
3936
4037class CreateGalaxy extends React.Component {
4138 constructor(props) {
42− super(props)
39+ super(props);
4340
4441 const galaxyOwner = need.address(props);
4542
@@ -47,126 +44,122 @@ class CreateGalaxy extends React.Component {
4744 galaxyOwner: galaxyOwner,
4845 galaxyName: '',
4946 isAvailable: null,
50− }
47+ };
5148
52− this.handleAddressInput = this.handleAddressInput.bind(this)
53− this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this)
54− this.confirmAvailability = this.confirmAvailability.bind(this)
55− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
49+ this.handleAddressInput = this.handleAddressInput.bind(this);
50+ this.handleGalaxyNameInput = this.handleGalaxyNameInput.bind(this);
51+ this.confirmAvailability = this.confirmAvailability.bind(this);
52+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
5653 this.statelessRef = React.createRef();
5754 }
5855
59− handleGalaxyNameInput = (galaxyName) => {
60− this.setState({ galaxyName, isAvailable: null })
61− this.statelessRef.current.clearTxn()
62− }
56+ handleGalaxyNameInput = galaxyName => {
57+ this.setState({ galaxyName, isAvailable: null });
58+ this.statelessRef.current.clearTxn();
59+ };
6360
64− handleAddressInput = (galaxyOwner) => {
65− this.setState({ galaxyOwner })
66− this.statelessRef.current.clearTxn()
67− }
61+ handleAddressInput = galaxyOwner => {
62+ this.setState({ galaxyOwner });
63+ this.statelessRef.current.clearTxn();
64+ };
6865
6966 createUnsignedTxn = () => {
70− const { state, props } = this
71− if (isValidAddress(state.galaxyOwner) === false) return Maybe.Nothing()
72− if (state.isAvailable === false) return Maybe.Nothing()
73− if (canDecodePatp(state.galaxyName) === false) return Maybe.Nothing()
74− if (isValidGalaxy(state.galaxyName) === false) return Maybe.Nothing()
67+ const { state, props } = this;
68+ if (isValidAddress(state.galaxyOwner) === false) return Maybe.Nothing();
69+ if (state.isAvailable === false) return Maybe.Nothing();
70+ if (canDecodePatp(state.galaxyName) === false) return Maybe.Nothing();
71+ if (isValidGalaxy(state.galaxyName) === false) return Maybe.Nothing();
7572
7673 const validContracts = need.contracts(props);
7774
78− const galaxyDec = parseInt(ob.patp2dec(state.galaxyName), 10)
75+ const galaxyDec = parseInt(ob.patp2dec(state.galaxyName), 10);
7976
8077 const txn = azimuth.ecliptic.createGalaxy(
8178 validContracts,
8279 galaxyDec,
8380 state.galaxyOwner
84− )
81+ );
8582
86− return Maybe.Just(txn)
87− }
83+ return Maybe.Just(txn);
84+ };
8885
8986 confirmAvailability = async () => {
90− const { state, props } = this
87+ const { state, props } = this;
9188
9289 if (canDecodePatp(state.galaxyName) === false) {
93− this.setState({ isAvailable: false })
94− return
90+ this.setState({ isAvailable: false });
91+ return;
9592 }
9693
9794 const validContracts = need.contracts(props);
9895
99− const galaxyDec = ob.patp2dec(state.galaxyName)
96+ const galaxyDec = ob.patp2dec(state.galaxyName);
10097
10198 const currentOwner = await azimuth.azimuth.getOwner(
10299 validContracts,
103100 galaxyDec
104− )
101+ );
105102
106− const available = eqAddr(currentOwner, ETH_ZERO_ADDR)
103+ const available = eqAddr(currentOwner, ETH_ZERO_ADDR);
107104
108− this.setState({ isAvailable: available })
109− }
105+ this.setState({ isAvailable: available });
106+ };
110107
111108 render() {
112− const { props, state } = this
109+ const { props, state } = this;
113110
114− const validAddress = isValidAddress(state.galaxyOwner)
115− const validGalaxy = isValidGalaxy(state.galaxyName)
111+ const validAddress = isValidAddress(state.galaxyOwner);
112+ const validGalaxy = isValidGalaxy(state.galaxyName);
116113
117− const canGenerate = validAddress === true &&
118− validGalaxy === true &&
119− state.isAvailable === true
114+ const canGenerate =
115+ validAddress === true &&
116+ validGalaxy === true &&
117+ state.isAvailable === true;
120118
121119 const esvisible =
122− props.networkType === NETWORK_NAMES.ROPSTEN ||
123− props.networkType === NETWORK_NAMES.MAINNET
120+ props.networkType === NETWORK_NAMES.ROPSTEN ||
121+ props.networkType === NETWORK_NAMES.MAINNET;
124122
125123 const esdomain =
126− props.networkType === NETWORK_NAMES.ROPSTEN
127− ? "ropsten.etherscan.io"
128− : "etherscan.io"
124+ props.networkType === NETWORK_NAMES.ROPSTEN
125+ ? 'ropsten.etherscan.io'
126+ : 'etherscan.io';
129127
130128 return (
131129 <Row>
132130 <Col>
133− <H1> { 'Create a Galaxy' } </H1>
131+ <H1> {'Create a Galaxy'} </H1>
134132
135133 <P>
136− {
137− 'Enter the galaxy to create and the address that will own ' +
138− 'it (defaulting to this account, if not provided).'
139− }
134+ {'Enter the galaxy to create and the address that will own ' +
135+ 'it (defaulting to this account, if not provided).'}
140136 </P>
141137
142138 <GalaxyInput
143− className='mono'
144− prop-size='lg'
145− prop-format='innerLabel'
139+ className="mono"
140+ prop-size="lg"
141+ prop-format="innerLabel"
146142 autoFocus
147− placeholder='e.g. ~zod'
143+ placeholder="e.g. ~zod"
148144 value={state.galaxyName}
149145 onChange={v => this.handleGalaxyNameInput(v)}>
150− <InnerLabel>{ 'Galaxy Name' }</InnerLabel>
146+ <InnerLabel>{'Galaxy Name'}</InnerLabel>
151147 <ValidatedSigil
152− className='tr-0 mt-05 mr-0 abs'
148+ className="tr-0 mt-05 mr-0 abs"
153149 patp={state.galaxyName}
154150 size={68}
155− margin={8} />
151+ margin={8}
152+ />
156153 </GalaxyInput>
157154
158155 <AddressInput
159− className='mono mt-8'
160− prop-size='lg'
161− prop-format='innerLabel'
162− value={ state.galaxyOwner }
163− onChange={ v => this.handleAddressInput(v) }>
164− <InnerLabel>
165− { 'Address that will own this galaxy' }
166− </InnerLabel>
167− <ShowBlockie
168− className={'mt-1'}
169− address={state.galaxyOwner} />
156+ className="mono mt-8"
157+ prop-size="lg"
158+ prop-format="innerLabel"
159+ value={state.galaxyOwner}
160+ onChange={v => this.handleAddressInput(v)}>
161+ <InnerLabel>{'Address that will own this galaxy'}</InnerLabel>
162+ <ShowBlockie className={'mt-1'} address={state.galaxyOwner} />
170163 </AddressInput>
171164
172165 <Anchor
@@ -175,12 +168,12 @@ class CreateGalaxy extends React.Component {
175168 prop-disabled={!validAddress || !esvisible}
176169 target={'_blank'}
177170 href={`https://${esdomain}/address/${state.galaxyOwner}`}>
178− {'View on Etherscan ↗'}
171+ {'View on Etherscan ↗'}
179172 </Anchor>
180173
181174 <Button
182− prop-size='lg wide'
183− className='mt-8'
175+ prop-size="lg wide"
176+ className="mt-8"
184177 prop-color={buttonTriState(state.isAvailable)}
185178 disabled={!validGalaxy}
186179 onClick={() => this.confirmAvailability()}>
@@ -193,15 +186,12 @@ class CreateGalaxy extends React.Component {
193186 // Other
194187 ref={this.statelessRef}
195188 canGenerate={canGenerate}
196− createUnsignedTxn={this.createUnsignedTxn} />
197−
189+ createUnsignedTxn={this.createUnsignedTxn}
190+ />
198191 </Col>
199192 </Row>
200− )
193+ );
201194 }
202195}
203196
204−
205−
206−
207−export default CreateGalaxy
197+export default CreateGalaxy;
src/bridge/views/GenKeyfile.js+56−67
@@ -1,27 +1,25 @@
1−import React from 'react'
2−import Maybe from 'folktale/maybe'
3−import * as need from '../lib/need'
1+import React from 'react';
2+import Maybe from 'folktale/maybe';
3+import * as need from '../lib/need';
44
5−import { Button } from '../components/Base'
6−import { Row, Col, H1, P } from '../components/Base'
5+import { Button } from '../components/Base';
6+import { Row, Col, H1, P } from '../components/Base';
77
8−import * as ob from 'urbit-ob'
9−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
10−import saveAs from 'file-saver'
8+import * as ob from 'urbit-ob';
9+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
10+import saveAs from 'file-saver';
1111
12−import { attemptSeedDerivation, genKey } from '../lib/keys'
13−import {
14− addHexPrefix
15− } from '../lib/wallet'
12+import { attemptSeedDerivation, genKey } from '../lib/keys';
13+import { addHexPrefix } from '../lib/wallet';
1614
1715class GenKeyfile extends React.Component {
1816 constructor(props) {
19− super(props)
17+ super(props);
2018
2119 this.state = {
2220 keyfile: '',
23− loaded: false
24− }
21+ loaded: false,
22+ };
2523 }
2624
2725 getPointDetails() {
@@ -31,116 +29,107 @@ class GenKeyfile extends React.Component {
3129
3230 // in case we did SetKeys earlier this session, make sure to generate the
3331 // newer keyfile, rather than the one that will expire soon
34− const revision = (this.props.networkRevisionCache ||
35− parseInt(pointDetails.keyRevisionNumber));
32+ const revision =
33+ this.props.networkRevisionCache ||
34+ parseInt(pointDetails.keyRevisionNumber);
3635
3736 return {
3837 point,
3938 pointDetails,
40− revision
41− }
39+ revision,
40+ };
4241 }
4342
4443 async componentDidMount() {
4544 const { point, pointDetails, revision } = this.getPointDetails();
46− let keyfile = ''
45+ let keyfile = '';
4746
48− const hexRegExp = /[0-9A-Fa-f]{64}/g
49− const networkSeed = await this.deriveSeed()
47+ const hexRegExp = /[0-9A-Fa-f]{64}/g;
48+ const networkSeed = await this.deriveSeed();
5049
51− const keysmatch = this.checkKeysMatch(networkSeed, pointDetails)
52− const seedValid = hexRegExp.test(networkSeed)
50+ const keysmatch = this.checkKeysMatch(networkSeed, pointDetails);
51+ const seedValid = hexRegExp.test(networkSeed);
5352
5453 if (keysmatch && seedValid) {
55− keyfile = genKey(networkSeed, point, revision)
54+ keyfile = genKey(networkSeed, point, revision);
5655 }
5756
5857 this.setState({
5958 keyfile: keyfile,
60− loaded: true
59+ loaded: true,
6160 });
6261 }
6362
6463 checkKeysMatch(networkSeed, pointDetails) {
65− const crypub = pointDetails.encryptionKey
66− const sgnpub = pointDetails.authenticationKey
64+ const crypub = pointDetails.encryptionKey;
65+ const sgnpub = pointDetails.authenticationKey;
6766
68− const { crypt, auth } = kg.deriveNetworkKeys(networkSeed)
67+ const { crypt, auth } = kg.deriveNetworkKeys(networkSeed);
6968
7069 const keysmatch =
71− crypub === addHexPrefix(crypt.public)
72− && sgnpub === addHexPrefix(auth.public)
70+ crypub === addHexPrefix(crypt.public) &&
71+ sgnpub === addHexPrefix(auth.public);
7372
7473 return keysmatch;
7574 }
7675
7776 async deriveSeed() {
78− const next = false
79− let seed = await attemptSeedDerivation(next, this.props)
77+ const next = false;
78+ let seed = await attemptSeedDerivation(next, this.props);
8079
8180 if (seed.getOrElse('') === '' && this.props.networkSeedCache) {
82− seed = Maybe.Just(this.props.networkSeedCache)
81+ seed = Maybe.Just(this.props.networkSeedCache);
8382 }
8483
85− return seed.getOrElse('')
84+ return seed.getOrElse('');
8685 }
8786
8887 render() {
8988 const { point, revision } = this.getPointDetails();
90− const { keyfile, loaded } = this.state
89+ const { keyfile, loaded } = this.state;
9190
9291 return (
9392 <Row>
9493 <Col className={'col-md-8'}>
95− <H1>{ 'Generate keyfile' }</H1>
94+ <H1>{'Generate keyfile'}</H1>
9695
97− <P>
98− { "Download a private key file for booting this point in Arvo." }
99− </P>
96+ <P>{'Download a private key file for booting this point in Arvo.'}</P>
10097
101− { keyfile === '' && !loaded &&
102− <P>
103− { "Generating keyfile..." }
104− </P>
105− }
98+ {keyfile === '' && !loaded && <P>{'Generating keyfile...'}</P>}
10699
107− { keyfile === '' && loaded &&
100+ {keyfile === '' && loaded && (
108101 <React.Fragment>
109102 <P>
110103 <b>Warning: </b>
111− { `No valid network seed detected. To generate a keyfile, you
104+ {`No valid network seed detected. To generate a keyfile, you
112105 must reset your networking keys, or try logging in with your
113− master ticket or management mnemonic.` }
106+ master ticket or management mnemonic.`}
114107 </P>
115108
116109 <P>
117− { `If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.` }
110+ {`If you've just reset your networking keys, you may need to wait for the transaction to go through. Check back shortly.`}
118111 </P>
119112 </React.Fragment>
120− }
113+ )}
121114
122− { keyfile !== '' &&
115+ {keyfile !== '' && (
123116 <React.Fragment>
124− <div className="pb-5 text-code keyfile">
125− { keyfile }
126− </div>
117+ <div className="pb-5 text-code keyfile">{keyfile}</div>
127118 <Button
128− onClick={
129− () => {
130− let blob = new Blob([keyfile], {type:"text/plain;charset=utf-8"});
131− saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`)
132− }
133− }>
134− Download →
135− </Button>
119+ onClick={() => {
120+ let blob = new Blob([keyfile], {
121+ type: 'text/plain;charset=utf-8',
122+ });
123+ saveAs(blob, `${ob.patp(point).slice(1)}-${revision}.key`);
124+ }}>
125+ Download →
126+ </Button>
136127 </React.Fragment>
137− }
138−
128+ )}
139129 </Col>
140130 </Row>
141− )
131+ );
142132 }
143−
144133}
145134
146−export default GenKeyfile
135+export default GenKeyfile;
src/bridge/views/InviteTicket.js+224−171
@@ -1,22 +1,36 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import { InnerLabel, Warning, TicketInput,
4− VerifyTicketInput, Button, Row, Col, Passport } from '../components/Base'
5−import * as azimuth from 'azimuth-js'
6−import * as need from '../lib/need'
7−
8−import { randomPatq } from '../lib/lib'
9−import { ROUTE_NAMES } from '../lib/router'
10−import { DEFAULT_HD_PATH, ownershipWalletFromTicket,
11− walletFromMnemonic } from '../lib/wallet'
12−import { BRIDGE_ERROR } from '../lib/error'
13−import { INVITE_STAGES, WALLET_STATES, TRANSACTION_STATES } from '../lib/invite'
14−import { generateWallet, startTransactions } from '../lib/invite'
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import {
4+ InnerLabel,
5+ Warning,
6+ TicketInput,
7+ VerifyTicketInput,
8+ Button,
9+ Row,
10+ Col,
11+ Passport,
12+} from '../components/Base';
13+import * as azimuth from 'azimuth-js';
14+import * as need from '../lib/need';
15+
16+import { randomPatq } from '../lib/lib';
17+import { ROUTE_NAMES } from '../lib/router';
18+import {
19+ DEFAULT_HD_PATH,
20+ ownershipWalletFromTicket,
21+ walletFromMnemonic,
22+} from '../lib/wallet';
23+import { BRIDGE_ERROR } from '../lib/error';
24+import {
25+ INVITE_STAGES,
26+ WALLET_STATES,
27+ TRANSACTION_STATES,
28+} from '../lib/invite';
29+import { generateWallet, startTransactions } from '../lib/invite';
1530
1631class InviteTicket extends React.Component {
17−
1832 constructor(props) {
19− super(props)
33+ super(props);
2034
2135 this.state = {
2236 inviteTicket: '',
@@ -29,46 +43,46 @@ class InviteTicket extends React.Component {
2943 walletStates: [],
3044 errors: [],
3145 stage: INVITE_STAGES.INVITE_LOGIN,
32− transactionProgress: TRANSACTION_STATES.GENERATING
33− }
46+ transactionProgress: TRANSACTION_STATES.GENERATING,
47+ };
3448
35− this.pointPlaceholder = randomPatq(4)
36− this.ticketPlaceholder = randomPatq(12)
49+ this.pointPlaceholder = randomPatq(4);
50+ this.ticketPlaceholder = randomPatq(12);
3751
38− this.handleInviteTicketInput = this.handleInviteTicketInput.bind(this)
39− this.handleVerifyTicketInput = this.handleVerifyTicketInput.bind(this)
40− this.updateProgress = this.updateProgress.bind(this)
41− this.pushWalletState = this.pushWalletState.bind(this)
42− this.navigateLogin = this.navigateLogin.bind(this)
52+ this.handleInviteTicketInput = this.handleInviteTicketInput.bind(this);
53+ this.handleVerifyTicketInput = this.handleVerifyTicketInput.bind(this);
54+ this.updateProgress = this.updateProgress.bind(this);
55+ this.pushWalletState = this.pushWalletState.bind(this);
56+ this.navigateLogin = this.navigateLogin.bind(this);
4357 }
4458
4559 pushWalletState(walletState) {
4660 this.setState({
47− walletStates: this.state.walletStates.concat(walletState)
48− })
61+ walletStates: this.state.walletStates.concat(walletState),
62+ });
4963 }
5064
5165 navigateLogin() {
52− this.props.popRoute()
53− this.props.pushRoute(ROUTE_NAMES.NETWORK)
66+ this.props.popRoute();
67+ this.props.pushRoute(ROUTE_NAMES.NETWORK);
5468 }
5569
5670 handleInviteTicketInput(inviteTicket) {
57− this.setState({ inviteTicket })
71+ this.setState({ inviteTicket });
5872 }
5973
6074 handleVerifyTicketInput(verifyTicket) {
61− this.setState({ verifyTicket })
75+ this.setState({ verifyTicket });
6276 }
6377
6478 updateTransactionProgress(transactionProgress) {
65− this.setState({ transactionProgress })
79+ this.setState({ transactionProgress });
6680 }
6781
6882 async unlockInviteWallet(inviteTicket) {
6983 this.setState({
7084 walletStates: this.state.walletStates.concat(WALLET_STATES.UNLOCKING),
71− stage: INVITE_STAGES.INVITE_WALLET
85+ stage: INVITE_STAGES.INVITE_WALLET,
7286 });
7387
7488 //NOTE ~zod because tmp wallet
@@ -81,178 +95,208 @@ class InviteTicket extends React.Component {
8195 ).matchWith({
8296 Just: w => w.value,
8397 Nothing: () => {
84− throw BRIDGE_ERROR.MISSING_WALLET
85− }
98+ throw BRIDGE_ERROR.MISSING_WALLET;
99+ },
86100 });
87101
88102 const addr = inviteWallet.address;
89103
90104 const ctrcs = need.contracts(this.props);
91105
92− const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr)
106+ const incoming = await azimuth.azimuth.getTransferringFor(ctrcs, addr);
93107
94108 this.setState({
95− walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING)
96− })
109+ walletStates: this.state.walletStates.concat(WALLET_STATES.GENERATING),
110+ });
97111
98− let realPoint = Nothing()
99− let realWallet = Nothing()
100− let errors = []
112+ let realPoint = Nothing();
113+ let realWallet = Nothing();
114+ let errors = [];
101115
102116 if (incoming.length > 0) {
103− let pointNum = parseInt(incoming[0], 10)
104− realPoint = Just(pointNum)
117+ let pointNum = parseInt(incoming[0], 10);
118+ realPoint = Just(pointNum);
105119
106120 this.setState({
107− realPoint
108− })
121+ realPoint,
122+ });
109123
110− let genWallet = await generateWallet(pointNum)
124+ let genWallet = await generateWallet(pointNum);
111125
112− realWallet = Just(genWallet)
126+ realWallet = Just(genWallet);
113127 if (incoming.length > 1) {
114128 //TODO notify user "...and others / ticket reusable"
115129 }
116130 } else {
117− errors = ["Invite code has no claimable ship. Check your invite code and try again."]
131+ errors = [
132+ 'Invite code has no claimable ship. Check your invite code and try again.',
133+ ];
118134 }
119135
120136 this.setState({
121137 realWallet,
122138 inviteWallet: Just(inviteWallet),
123139 // dev mode: uncomment this out to autofill verified ticket
124− verifyTicket: !Nothing.hasInstance(realWallet) ? realWallet.value.ticket : '',
140+ verifyTicket: !Nothing.hasInstance(realWallet)
141+ ? realWallet.value.ticket
142+ : '',
125143 walletStates: this.state.walletStates.concat(WALLET_STATES.RENDERING),
126− errors
127− })
144+ errors,
145+ });
128146 }
129147
130148 getTicketElement() {
131− const phTick = this.ticketPlaceholder
149+ const phTick = this.ticketPlaceholder;
132150
133− let isLogin = this.state.stage === INVITE_STAGES.INVITE_LOGIN
134− let isVerify = this.state.stage === INVITE_STAGES.INVITE_VERIFY
151+ let isLogin = this.state.stage === INVITE_STAGES.INVITE_LOGIN;
152+ let isVerify = this.state.stage === INVITE_STAGES.INVITE_VERIFY;
135153
136154 if (isLogin || isVerify) {
137−
138− let value = isLogin ? this.state.inviteTicket : this.state.verifyTicket
139− let handler = isLogin ? this.handleInviteTicketInput : this.handleVerifyTicketInput
140− let label = isLogin ? 'Activation code' : 'ticket'
155+ let value = isLogin ? this.state.inviteTicket : this.state.verifyTicket;
156+ let handler = isLogin
157+ ? this.handleInviteTicketInput
158+ : this.handleVerifyTicketInput;
159+ let label = isLogin ? 'Activation code' : 'ticket';
141160
142161 // if realWallet is Just, then VerifyTicketInput appropriate contents; if Nothing, remain Nothing
143162 const Verified = this.state.realWallet.map(realWallet =>
144163 VerifyTicketInput(realWallet.ticket)
145− )
164+ );
146165
147166 // Verified.getOrElse(TicketInput) falls back to TicketInput if Verified is Nothing
148− const TicketElem =
149− isVerify
167+ const TicketElem = isVerify
150168 ? Verified.getOrElse(TicketInput)
151− : TicketInput
169+ : TicketInput;
152170
153171 return (
154172 <TicketElem
155− className='mono mt-8'
156− prop-size='md'
157− prop-format='innerLabel'
158− type='text'
173+ className="mono mt-8"
174+ prop-size="md"
175+ prop-format="innerLabel"
176+ type="text"
159177 autoFocus
160− name='invite'
161− placeholder={ `e.g. ${phTick}` }
162− value={ value }
163− onChange={ handler }>
164− <InnerLabel>{ label }</InnerLabel>
178+ name="invite"
179+ placeholder={`e.g. ${phTick}`}
180+ value={value}
181+ onChange={handler}>
182+ <InnerLabel>{label}</InnerLabel>
165183 </TicketElem>
166− )
184+ );
167185 } else {
168− return null
186+ return null;
169187 }
170188 }
171189
172190 getStageText() {
173− const { stage } = this.state
191+ const { stage } = this.state;
174192
175193 let progressWidth = this.state.transactionProgress.pct;
176194
177− const stageText = stage === INVITE_STAGES.INVITE_LOGIN
178− ? <h1 className="text-700 mt-15">Welcome</h1>
179− : stage === INVITE_STAGES.INVITE_WALLET
180− ? (
195+ const stageText =
196+ stage === INVITE_STAGES.INVITE_LOGIN ? (
197+ <h1 className="text-700 mt-15">Welcome</h1>
198+ ) : stage === INVITE_STAGES.INVITE_WALLET ? (
199+ <div>
200+ <h1 className="fs-6 lh-8 mb-3">Passport</h1>
201+ <p>
202+ A passport is your digital identity. You will use your passport to
203+ access your true computer, send payments, and administer your
204+ identity. So naturally, you must keep this secure.
205+ </p>
206+ <p>
207+ After you’ve downloaded your passport, back up the ticket manually
208+ or store on a trusted device.
209+ </p>
210+ </div>
211+ ) : stage === INVITE_STAGES.INVITE_VERIFY ? (
212+ <div>
213+ <h1 className="fs-6 lh-8 mb-3">Verify Passport</h1>
214+ <p>
215+ Afer you download your passport, verify your custody. Your passport
216+ should be a folder of image files. One of them is your Master
217+ Ticket. Open it and enter the 4 word phrase below (with hyphens).
218+ </p>
219+ </div>
220+ ) : INVITE_STAGES.INVITE_TRANSACTIONS ? (
221+ this.state.transactionProgress.label ===
222+ TRANSACTION_STATES.DONE.label ? (
181223 <div>
182− <h1 className="fs-6 lh-8 mb-3">Passport</h1>
183− <p>A passport is your digital identity. You will use your passport to access your true computer, send payments, and administer your identity. So naturally, you must keep this secure.</p>
184− <p>After you’ve downloaded your passport, back up the ticket manually or store on a trusted device.</p>
224+ <h1 className="fs-6 lh-8 mb-3">
225+ <span>Success</span>
226+ <span className="ml-4 green">✓</span>
227+ </h1>
228+ <p>Transferring to Bridge...</p>
185229 </div>
186− )
187− : stage === INVITE_STAGES.INVITE_VERIFY
188− ? (
230+ ) : (
189231 <div>
190− <h1 className="fs-6 lh-8 mb-3">Verify Passport</h1>
191− <p>Afer you download your passport, verify your custody. Your passport should be a folder of image files. One of them is your Master Ticket. Open it and enter the 4 word phrase below (with hyphens).</p>
192− </div>
193− ) : INVITE_STAGES.INVITE_TRANSACTIONS
194− ? this.state.transactionProgress.label === TRANSACTION_STATES.DONE.label
195− ? (
196− <div>
197− <h1 className="fs-6 lh-8 mb-3">
198− <span>Success</span>
199− <span className="ml-4 green">✓</span>
200− </h1>
201− <p>Transferring to Bridge...</p>
232+ <h1 className="fs-6 lh-8 mb-3">Submitting</h1>
233+ <p className="mt-4 mb-4">
234+ This step can take up to five minutes. Please do not leave this
235+ page until the transactions are complete.
236+ </p>
237+ <div className="passport-progress mb-2">
238+ <div
239+ className="passport-progress-filled"
240+ style={{ width: progressWidth }}></div>
202241 </div>
203− )
204− : (
205− <div>
206− <h1 className="fs-6 lh-8 mb-3">Submitting</h1>
207− <p className="mt-4 mb-4">This step can take up to five minutes. Please do not leave this page until the transactions are complete.</p>
208− <div className="passport-progress mb-2">
209− <div className="passport-progress-filled" style={{width: progressWidth}}></div>
210− </div>
211− <div className="flex justify-between">
212− <div className="text-mono text-sm lh-6 green-dark">{this.state.transactionProgress.label}</div>
242+ <div className="flex justify-between">
243+ <div className="text-mono text-sm lh-6 green-dark">
244+ {this.state.transactionProgress.label}
213245 </div>
214246 </div>
215− )
216− : null
247+ </div>
248+ )
249+ ) : null;
217250
218− return stageText
251+ return stageText;
219252 }
220253
221254 updateProgress(notification) {
222− let newState = notification.type === "progress"
255+ let newState =
256+ notification.type === 'progress'
223257 ? { transactionProgress: notification.value }
224− : notification.type === "notify"
258+ : notification.type === 'notify'
225259 ? { errors: [notification.value] }
226− : {}
260+ : {};
227261
228− this.setState(newState)
262+ this.setState(newState);
229263 }
230264
231265 getContinueButton() {
232− const { realPoint, realWallet, inviteWallet, stage, walletStates,
233− verifyTicket, inviteTicket } = this.state
234− const { web3, contracts, setUrbitWallet } = this.props
266+ const {
267+ realPoint,
268+ realWallet,
269+ inviteWallet,
270+ stage,
271+ walletStates,
272+ verifyTicket,
273+ inviteTicket,
274+ } = this.state;
275+ const { web3, contracts, setUrbitWallet } = this.props;
235276
236277 const btnColor = this.state.walletStates.includes(WALLET_STATES.PAPER_READY)
237278 ? 'green'
238− : 'black'
279+ : 'black';
239280
240− const clickHandler = stage === INVITE_STAGES.INVITE_LOGIN
281+ const clickHandler =
282+ stage === INVITE_STAGES.INVITE_LOGIN
241283 ? () => {
242− this.unlockInviteWallet(inviteTicket)
284+ this.unlockInviteWallet(inviteTicket);
243285 }
244286 : stage === INVITE_STAGES.INVITE_WALLET
245287 ? () => {
246288 this.setState({
247− stage: INVITE_STAGES.INVITE_VERIFY
248− })
289+ stage: INVITE_STAGES.INVITE_VERIFY,
290+ });
249291 }
250292 : stage === INVITE_STAGES.INVITE_VERIFY
251293 ? () => {
252294 this.setState({
253295 stage: INVITE_STAGES.INVITE_TRANSACTIONS,
254− walletStates: this.state.walletStates.concat(WALLET_STATES.TRANSACTIONS)
255− })
296+ walletStates: this.state.walletStates.concat(
297+ WALLET_STATES.TRANSACTIONS
298+ ),
299+ });
256300
257301 startTransactions({
258302 inviteWalletM: inviteWallet,
@@ -262,77 +306,87 @@ class InviteTicket extends React.Component {
262306 web3M: web3,
263307 contractsM: contracts,
264308 setUrbitWallet,
265− updateProgress: this.updateProgress
266− })
267− .then(() => {
309+ updateProgress: this.updateProgress,
310+ }).then(() => {
268311 setTimeout(() => {
269− this.props.popRoute()
270− }, 5000)
271− })
312+ this.props.popRoute();
313+ }, 5000);
314+ });
272315 }
273− : null
316+ : null;
274317
275318 const ticketMatches = this.state.realWallet.matchWith({
276319 Just: wal => wal.value.ticket === this.state.verifyTicket,
277− Nothing: () => false
278− })
320+ Nothing: () => false,
321+ });
279322
280− const continueReady = stage === INVITE_STAGES.INVITE_WALLET
323+ const continueReady =
324+ stage === INVITE_STAGES.INVITE_WALLET
281325 ? walletStates.includes(WALLET_STATES.DOWNLOADED)
282326 : stage === INVITE_STAGES.INVITE_VERIFY
283327 ? ticketMatches
284− : true
285−
286− const buttonElem = stage === INVITE_STAGES.INVITE_TRANSACTIONS
287− ? null
288− : (
289− <Button
290− className={'mt-4'}
291− prop-size={'lg'}
292− prop-color={btnColor}
293− disabled={!continueReady}
294− onClick={clickHandler}
295− >
296− { 'Continue →' }
297− </Button>
298− )
299−
300− return buttonElem
328+ : true;
329+
330+ const buttonElem =
331+ stage === INVITE_STAGES.INVITE_TRANSACTIONS ? null : (
332+ <Button
333+ className={'mt-4'}
334+ prop-size={'lg'}
335+ prop-color={btnColor}
336+ disabled={!continueReady}
337+ onClick={clickHandler}>
338+ {'Continue →'}
339+ </Button>
340+ );
341+
342+ return buttonElem;
301343 }
302344
303345 getStageDisplay() {
304− const { stage } = this.state
346+ const { stage } = this.state;
305347
306− if (stage === INVITE_STAGES.INVITE_LOGIN) return null
348+ if (stage === INVITE_STAGES.INVITE_LOGIN) return null;
307349
308350 return (
309351 <div className="mt-6 mb-8">
310− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET && 'gray-20'}`}>1 Passport</span>
311− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY && 'gray-20'}`}>2 Verify</span>
312− <span className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_TRANSACTIONS && 'gray-20'}`}>3 Invite</span>
352+ <span
353+ className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_WALLET &&
354+ 'gray-20'}`}>
355+ 1 Passport
356+ </span>
357+ <span
358+ className={`fs-35 mr-3 ${stage !== INVITE_STAGES.INVITE_VERIFY &&
359+ 'gray-20'}`}>
360+ 2 Verify
361+ </span>
362+ <span
363+ className={`fs-35 mr-3 ${stage !==
364+ INVITE_STAGES.INVITE_TRANSACTIONS && 'gray-20'}`}>
365+ 3 Invite
366+ </span>
313367 </div>
314− )
368+ );
315369 }
316370
317371 getErrors() {
318− if (this.state.errors.length === 0) return null
372+ if (this.state.errors.length === 0) return null;
319373
320− let errorElems = this.state.errors.map(e => <span>{e}</span>)
374+ let errorElems = this.state.errors.map(e => <span>{e}</span>);
321375
322376 return (
323377 <Warning>
324378 <h3 className={'mb-2'}>{'Warning'}</h3>
325− { errorElems }
379+ {errorElems}
326380 </Warning>
327− )
381+ );
328382 }
329383
330384 render() {
331− let stageDisplay = this.getStageDisplay()
332− let stageText = this.getStageText()
333− let ticketElem = this.getTicketElement()
334− let continueButton = this.getContinueButton()
335− let errorDisplay = this.getErrors()
385+ let stageDisplay = this.getStageDisplay();
386+ let stageText = this.getStageText();
387+ let ticketElem = this.getTicketElement();
388+ let continueButton = this.getContinueButton();
389+ let errorDisplay = this.getErrors();
336390
337391 return (
338392 <Row>
@@ -343,16 +397,15 @@ class InviteTicket extends React.Component {
343397 {ticketElem}
344398 {continueButton}
345399
346− { this.state.stage === INVITE_STAGES.INVITE_LOGIN &&
400+ {this.state.stage === INVITE_STAGES.INVITE_LOGIN && (
347401 <Button
348402 prop-type={`link`}
349403 prop-size={`sm`}
350404 className={`block mt-4`}
351− onClick={this.navigateLogin} >
405+ onClick={this.navigateLogin}>
352406 {`Log in`}
353407 </Button>
354− }
355−
408+ )}
356409 </Col>
357410 <Col className="col-md-offset-3 col-md-5 mt-18">
358411 <Passport
@@ -363,8 +416,8 @@ class InviteTicket extends React.Component {
363416 />
364417 </Col>
365418 </Row>
366− )
419+ );
367420 }
368421}
369422
370−export default InviteTicket
423+export default InviteTicket;
src/bridge/views/InvitesManage.js+76−69
@@ -1,18 +1,23 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import * as ob from 'urbit-ob'
4−import * as azimuth from 'azimuth-js'
5−import * as need from '../lib/need'
6−
7−import { Row, Col, Warning, Input,
8− PointInput, InnerLabel, ValidatedSigil } from '../components/Base'
9−import StatelessTransaction from '../components/StatelessTransaction'
10−
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import * as ob from 'urbit-ob';
4+import * as azimuth from 'azimuth-js';
5+import * as need from '../lib/need';
6+
7+import {
8+ Row,
9+ Col,
10+ Warning,
11+ Input,
12+ PointInput,
13+ InnerLabel,
14+ ValidatedSigil,
15+} from '../components/Base';
16+import StatelessTransaction from '../components/StatelessTransaction';
1117
1218class InvitesManage extends React.Component {
13−
1419 constructor(props) {
15− super(props)
20+ super(props);
1621
1722 this.state = {
1823 invitesWillWork: Nothing(),
@@ -20,8 +25,8 @@ class InvitesManage extends React.Component {
2025 targetPlanet: Nothing(),
2126 currentPoolSize: Nothing(),
2227 cachedPoolSizes: {},
23− targetPoolSize: 5
24− }
28+ targetPoolSize: 5,
29+ };
2530
2631 this.checkCurrentInvites = this.checkCurrentInvites.bind(this);
2732 this.handlePointInput = this.handlePointInput.bind(this);
@@ -35,13 +40,15 @@ class InvitesManage extends React.Component {
3540 this.point = need.pointCursor(this.props);
3641 this.contracts = need.contracts(this.props);
3742
38− azimuth.azimuth.isSpawnProxy(
39− this.contracts,
40− this.point,
41− this.contracts.delegatedSending.address
42− ).then(isSpawnProxy => {
43− this.setState({invitesWillWork:Just(isSpawnProxy)});
44− });
43+ azimuth.azimuth
44+ .isSpawnProxy(
45+ this.contracts,
46+ this.point,
47+ this.contracts.delegatedSending.address
48+ )
49+ .then(isSpawnProxy => {
50+ this.setState({ invitesWillWork: Just(isSpawnProxy) });
51+ });
4552 }
4653
4754 componentDidUpdate(prevProps) {
@@ -53,10 +60,9 @@ class InvitesManage extends React.Component {
5360 targetPlanet: Nothing(),
5461 planetInput: targetPlanet,
5562 canGenerate: false,
56− currentPoolSize: Nothing()
57− }
58− if ((targetPlanet.length === 14)
59− && ob.isValidPatp(targetPlanet)) {
63+ currentPoolSize: Nothing(),
64+ };
65+ if (targetPlanet.length === 14 && ob.isValidPatp(targetPlanet)) {
6066 const target = ob.patp2dec(targetPlanet);
6167 //TODO there must be a simpler way to do this check
6268 const isChild = ob.sein(targetPlanet) === ob.patp(this.point);
@@ -74,12 +80,12 @@ class InvitesManage extends React.Component {
7480 if (typeof cachedPoolSizes[who] !== 'undefined') {
7581 return Just(cachedPoolSizes[who]);
7682 } else {
77− azimuth.delegatedSending.getPool(this.contracts, who)
78− .then(pool => {
79− azimuth.delegatedSending.invitesInPool(this.contracts, pool, this.point)
80− .then(size => {
81− this.updatePoolSize(who, size);
82− });
83+ azimuth.delegatedSending.getPool(this.contracts, who).then(pool => {
84+ azimuth.delegatedSending
85+ .invitesInPool(this.contracts, pool, this.point)
86+ .then(size => {
87+ this.updatePoolSize(who, size);
88+ });
8389 });
8490 return Nothing();
8591 }
@@ -90,75 +96,76 @@ class InvitesManage extends React.Component {
9096 cache[who] = size;
9197 this.setState({ cachedPoolSizes: cache });
9298 const target = this.state.targetPlanet;
93− const match = Just.hasInstance(target) ? (who === target.value) : false;
99+ const match = Just.hasInstance(target) ? who === target.value : false;
94100 if (match) {
95101 this.setState({ currentPoolSize: Just(size) });
96102 }
97103 }
98104
99105 handlePoolSizeInput(targetSize) {
100− this.setState({targetPoolSize: targetSize});
106+ this.setState({ targetPoolSize: targetSize });
101107 }
102108
103109 createUnsignedTxn() {
104− return Just(azimuth.delegatedSending.setPoolSize(
105− this.contracts,
106− this.point,
107− this.state.targetPlanet.value,
108− this.state.targetPoolSize
109− ))
110+ return Just(
111+ azimuth.delegatedSending.setPoolSize(
112+ this.contracts,
113+ this.point,
114+ this.state.targetPlanet.value,
115+ this.state.targetPoolSize
116+ )
117+ );
110118 }
111119
112120 render() {
113−
114121 let spawnProxyWarning = null;
115122 if (this.state.invitesWillWork.value === false) {
116123 spawnProxyWarning = (
117124 <Warning>
118125 <h3 className={'mb-2'}>{'Warning'}</h3>
119− {
120− 'Planets under this star will not be able to send invites ' +
121− 'until the invite contract ('
122− }<code>{this.contracts.delegatedSending.address}</code>{
123− ') is made spawn proxy for this star.'
124− }
126+ {'Planets under this star will not be able to send invites ' +
127+ 'until the invite contract ('}
128+ <code>{this.contracts.delegatedSending.address}</code>
129+ {') is made spawn proxy for this star.'}
125130 </Warning>
126131 );
127132 }
128133
129134 let poolSizeText = this.state.currentPoolSize.matchWith({
130135 Just: ps => `(currently ${ps.value})`,
131− Nothing: _ => ''
136+ Nothing: _ => '',
132137 });
133138
134139 return (
135140 <Row>
136141 <Col>
142+ <p>
143+ {
144+ 'manage invites here, for stars. can only give invites to child planets'
145+ }
146+ </p>
137147
138− <p>{ 'manage invites here, for stars. can only give invites to child planets' }</p>
139−
140− { spawnProxyWarning }
148+ {spawnProxyWarning}
141149
142150 <PointInput
143− prop-format='innerlabel'
144− prop-size='lg'
145− placeholder={ '~sampel-sipnem' }
146− value={ this.state.planetInput }
147− onChange={ this.handlePointInput }>
148− <InnerLabel>{ 'Planet to set invites for' }</InnerLabel>
151+ prop-format="innerlabel"
152+ prop-size="lg"
153+ placeholder={'~sampel-sipnem'}
154+ value={this.state.planetInput}
155+ onChange={this.handlePointInput}>
156+ <InnerLabel>{'Planet to set invites for'}</InnerLabel>
149157 <ValidatedSigil
150− patp={ this.state.targetPlanet }
151− validator={() => this.validatePoint} />
158+ patp={this.state.targetPlanet}
159+ validator={() => this.validatePoint}
160+ />
152161 </PointInput>
153162
154163 <Input
155− prop-format='innerlabel'
156− prop-size='lg'
157− value={ this.state.targetPoolSize }
158− onChange={ this.handlePoolSizeInput }>
159− <InnerLabel>
160− { `Available invites ${poolSizeText}` }
161− </InnerLabel>
164+ prop-format="innerlabel"
165+ prop-size="lg"
166+ value={this.state.targetPoolSize}
167+ onChange={this.handlePoolSizeInput}>
168+ <InnerLabel>{`Available invites ${poolSizeText}`}</InnerLabel>
162169 </Input>
163170
164171 <StatelessTransaction
@@ -167,12 +174,12 @@ class InvitesManage extends React.Component {
167174 // Other
168175 canGenerate={this.state.canGenerate}
169176 createUnsignedTxn={this.createUnsignedTxn}
170− ref={this.statelessRef} />
171−
177+ ref={this.statelessRef}
178+ />
172179 </Col>
173180 </Row>
174− )
181+ );
175182 }
176183}
177184
178−export default InvitesManage
185+export default InvitesManage;
src/bridge/views/InvitesSend.js+154−129
@@ -1,13 +1,13 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import * as azimuth from 'azimuth-js'
4−import * as need from '../lib/need'
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import * as azimuth from 'azimuth-js';
4+import * as need from '../lib/need';
55
6−import { hasReceived, sendMail } from '../lib/inviteMail'
7−import { Row, Col, Button, Input, H3, Warning } from '../components/Base'
6+import { hasReceived, sendMail } from '../lib/inviteMail';
7+import { Row, Col, Button, Input, H3, Warning } from '../components/Base';
88
99// for wallet generation
10−import * as wg from '../../walletgen/lib/lib'
10+import * as wg from '../../walletgen/lib/lib';
1111
1212// for transaction generation and signing
1313import {
@@ -16,9 +16,9 @@ import {
1616 waitForTransactionConfirm,
1717 hexify,
1818 fromWei,
19− toWei
20−} from '../lib/txn'
21−import * as tank from '../lib/tank'
19+ toWei,
20+} from '../lib/txn';
21+import * as tank from '../lib/tank';
2222
2323const GAS_PRICE_GWEI = 20; // we pay the premium for faster ux
2424const GAS_LIMIT = 350000;
@@ -31,19 +31,18 @@ const STATUS = {
3131 FUNDING: 'Funding invites',
3232 SENDING: 'Sending invites',
3333 DONE: 'Done',
34− FAIL: 'Some failed'
35−}
34+ FAIL: 'Some failed',
35+};
3636
3737const EMAIL_STATUS = {
3838 BUSY: '⋯',
3939 DONE: '✔',
40− FAIL: '×'
41−}
40+ FAIL: '×',
41+};
4242
4343class InvitesSend extends React.Component {
44−
4544 constructor(props) {
46− super(props)
45+ super(props);
4746
4847 this.state = {
4948 invitesAvailable: Nothing(),
@@ -53,8 +52,8 @@ class InvitesSend extends React.Component {
5352 errors: Nothing(),
5453 //
5554 fundingMessage: Nothing(),
56− wipInvites: []
57− }
55+ wipInvites: [],
56+ };
5857
5958 this.findInvited = this.findInvited.bind(this);
6059 this.generateInvites = this.generateInvites.bind(this);
@@ -75,10 +74,11 @@ class InvitesSend extends React.Component {
7574 this.web3 = need.web3(this.props);
7675
7776 console.log(azimuth.delegatedSending);
78− azimuth.delegatedSending.getTotalUsableInvites(this.contracts, this.point)
79− .then(count => {
80− this.setState({invitesAvailable: Just(count)});
81− });
77+ azimuth.delegatedSending
78+ .getTotalUsableInvites(this.contracts, this.point)
79+ .then(count => {
80+ this.setState({ invitesAvailable: Just(count) });
81+ });
8282 this.findInvited();
8383 }
8484
@@ -93,19 +93,19 @@ class InvitesSend extends React.Component {
9393 );
9494 invited = invited.map(async point => {
9595 const active = await azimuth.azimuth.isActive(this.contracts, point);
96− const res = {point: Number(point), active:active};
96+ const res = { point: Number(point), active: active };
9797 return res;
9898 });
9999 invited = await Promise.all(invited);
100100 const total = invited.length;
101101 const accepted = invited.filter(i => i.active).length;
102− this.setState({invited: Just({total, accepted})});
102+ this.setState({ invited: Just({ total, accepted }) });
103103 }
104104
105105 async getTemporaryWallet() {
106106 const ticket = await wg.makeTicket(0x10000); // planet-sized ticket
107107 const owner = await wg.generateOwnershipWallet(0, ticket);
108− return {ticket, owner: owner.keys.address};
108+ return { ticket, owner: owner.keys.address };
109109 }
110110
111111 async generateInvites() {
@@ -114,13 +114,15 @@ class InvitesSend extends React.Component {
114114 const web3 = this.web3;
115115 const targets = this.state.targets;
116116 const planets = await azimuth.delegatedSending.getPlanetsToSend(
117− this.contracts, this.point, targets.length
117+ this.contracts,
118+ this.point,
119+ targets.length
118120 );
119121
120122 // account for the race condition where invites got used up while we were
121123 // composing our target list
122124 if (planets.length < targets.length) {
123− this.addError('Can currently only send '+planets.length+' invites!');
125+ this.addError('Can currently only send ' + planets.length + ' invites!');
124126 return;
125127 }
126128
@@ -137,7 +139,10 @@ class InvitesSend extends React.Component {
137139 const wallet = await this.getTemporaryWallet();
138140
139141 let inviteTx = azimuth.delegatedSending.sendPoint(
140− this.contracts, this.point, planets[i], wallet.owner
142+ this.contracts,
143+ this.point,
144+ planets[i],
145+ wallet.owner
141146 );
142147 const signedTx = await signTransaction({
143148 ...this.props,
@@ -146,11 +151,11 @@ class InvitesSend extends React.Component {
146151 gasLimit: GAS_LIMIT.toString(),
147152 nonce: nonce + i,
148153 chainId: chainId,
149− setStx: () => {}
154+ setStx: () => {},
150155 });
151156 const rawTx = hexify(signedTx.serialize());
152157
153− invites.push({email, ticket: wallet.ticket, signedTx, rawTx });
158+ invites.push({ email, ticket: wallet.ticket, signedTx, rawTx });
154159 this.setEmailStatus(i, EMAIL_STATUS.DONE);
155160 }
156161
@@ -164,14 +169,21 @@ class InvitesSend extends React.Component {
164169 //TODO invite status isn't changing/re-rendering... why?
165170 this.setState({
166171 status: STATUS.FUNDING,
167− invites: invites.map(i => { return {...i, status: Nothing()}; })
172+ invites: invites.map(i => {
173+ return { ...i, status: Nothing() };
174+ }),
168175 });
169176
170177 const tankWasUsed = await tank.ensureFundsFor(
171− web3, this.point, this.address,
172− (INVITE_COST * invites.length),
173− invites.map(i => i.rawTx), this.askForFunding,
174− () => { this.setState({ fundingMessage: Nothing() }); }
178+ web3,
179+ this.point,
180+ this.address,
181+ INVITE_COST * invites.length,
182+ invites.map(i => i.rawTx),
183+ this.askForFunding,
184+ () => {
185+ this.setState({ fundingMessage: Nothing() });
186+ }
175187 );
176188
177189 this.setState({ status: STATUS.SENDING });
@@ -185,31 +197,38 @@ class InvitesSend extends React.Component {
185197 let txHash;
186198 try {
187199 txHash = await sendSignedTransaction(
188− web3, Just(invite.signedTx), tankWasUsed, ()=>{}
200+ web3,
201+ Just(invite.signedTx),
202+ tankWasUsed,
203+ () => {}
189204 );
190− } catch(err) {
205+ } catch (err) {
191206 console.error('invite tx sending failed', err);
192207 this.addError('Invite transaction not sent for ' + invite.email);
193208 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
194209 continue;
195− };
210+ }
196211
197212 // ask the email sender to send this. we can do this prior to tx confirm,
198213 // because the sender service waits for confirm & success for us.
199214 // if this fails, inform the user and skip to next
200215 try {
201216 const mailSuccess = await sendMail(
202− invite.email, invite.ticket, invite.rawTx
217+ invite.email,
218+ invite.ticket,
219+ invite.rawTx
203220 );
204221 if (!mailSuccess) throw new Error();
205222 this.setEmailStatus(i, EMAIL_STATUS.DONE);
206− } catch(e) {
223+ } catch (e) {
207224 console.log('email send failed');
208225 //NOTE this assumes that the transaction succeeds, but we don't know
209226 // that for a fact yet...
210227 this.addError(
211− 'Invite email failed to send for ' + invite.email +
212− '. Please give them this ticket: ' + invite.ticket
228+ 'Invite email failed to send for ' +
229+ invite.email +
230+ '. Please give them this ticket: ' +
231+ invite.ticket
213232 );
214233 this.setEmailStatus(i, EMAIL_STATUS.FAIL);
215234 continue;
@@ -217,8 +236,7 @@ class InvitesSend extends React.Component {
217236
218237 // update status on transaction confirm, but don't block on it
219238 promises.push(
220− waitForTransactionConfirm(web3, txHash)
221− .then(success => {
239+ waitForTransactionConfirm(web3, txHash).then(success => {
222240 if (success) {
223241 this.setEmailStatus(i, EMAIL_STATUS.DONE);
224242 } else {
@@ -229,7 +247,6 @@ class InvitesSend extends React.Component {
229247 return success;
230248 })
231249 );
232−
233250 }
234251
235252 const txStatus = await Promise.all(promises);
@@ -249,17 +266,24 @@ class InvitesSend extends React.Component {
249266 addError(error) {
250267 const newError = this.state.errors.matchWith({
251268 Nothing: () => [error],
252− Just: errs => { errs.value.push(error); return errs.value; }
269+ Just: errs => {
270+ errs.value.push(error);
271+ return errs.value;
272+ },
253273 });
254274 this.setState({ errors: Just(newError) });
255275 }
256276
257277 askForFunding(address, minBalance, balance) {
258− this.setState({ fundingMessage: Just(
259− `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
260− `we'll continue once that's true. Current balance: ${fromWei(balance)}.` +
261− `Waiting...`
262− ) });
278+ this.setState({
279+ fundingMessage: Just(
280+ `Please make sure ${address} has at least ${fromWei(minBalance)} ETH,` +
281+ `we'll continue once that's true. Current balance: ${fromWei(
282+ balance
283+ )}.` +
284+ `Waiting...`
285+ ),
286+ });
263287 }
264288
265289 handleEmailInput(i, email) {
@@ -268,8 +292,7 @@ class InvitesSend extends React.Component {
268292
269293 // if it's a valid address, check if it has received an invite before
270294 if (email.match(/.*@.*\...+/)) {
271− hasReceived(email)
272− .then(res => {
295+ hasReceived(email).then(res => {
273296 targets[i].hasReceived = Just(res);
274297 this.setState({ targets });
275298 });
@@ -284,7 +307,7 @@ class InvitesSend extends React.Component {
284307 const targets = this.state.targets;
285308
286309 // may not have duplicate targets
287− if ((new Set(targets.map(t => t.email))).size !== targets.length)
310+ if (new Set(targets.map(t => t.email)).size !== targets.length)
288311 return false;
289312
290313 // none may have received invites already
@@ -292,14 +315,14 @@ class InvitesSend extends React.Component {
292315 const target = targets[i];
293316 const res = target.hasReceived.matchWith({
294317 Nothing: () => true,
295− Just: has => has.value
318+ Just: has => has.value,
296319 });
297320 if (res) return false;
298321 }
299322
300323 return this.state.invitesAvailable.matchWith({
301324 Nothing: () => false,
302− Just: invites => (invites.value > 0)
325+ Just: invites => invites.value > 0,
303326 });
304327 }
305328
@@ -316,46 +339,50 @@ class InvitesSend extends React.Component {
316339 }
317340
318341 render() {
319−
320− const dif = (this.state.status === STATUS.DONE)
321− ? this.state.targets.length
322− : null;
342+ const dif =
343+ this.state.status === STATUS.DONE ? this.state.targets.length : null;
323344
324345 const invitesAvailable = this.state.invitesAvailable.matchWith({
325346 Nothing: () => 'Loading...',
326− Just: (invites) => {
347+ Just: invites => {
327348 const dia = dif ? `(-${dif})` : '';
328349 return `You currently have ${invites.value}${dia} invitations left.`;
329− }
350+ },
330351 });
331352
332353 const invitesSent = this.state.invited.matchWith({
333− Nothing: () => (<>{'Loading...'}</>),
334− Just: (invited) => {
354+ Nothing: () => <>{'Loading...'}</>,
355+ Just: invited => {
335356 const dit = dif ? `(+${dif})` : '';
336− return (<>
337− {'Out of the '}
338− {invited.value.total + dit}
339− {' invites you sent, '}
340− {invited.value.accepted}
341− {' have been accepted.'}
342− </>);
343− }
357+ return (
358+ <>
359+ {'Out of the '}
360+ {invited.value.total + dit}
361+ {' invites you sent, '}
362+ {invited.value.accepted}
363+ {' have been accepted.'}
364+ </>
365+ );
366+ },
344367 });
345368
346369 const error = this.state.errors.matchWith({
347370 Nothing: () => null,
348− Just: (errors) => (<Warning className={'mt-8'}>
349− <H3 style={{marginTop: 0, paddingTop: 0}}>
350− { 'Something went wrong!' }
351− </H3>
352− { errors.value.map(err => (<p>{err}</p>)) }
353− </Warning>)
371+ Just: errors => (
372+ <Warning className={'mt-8'}>
373+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
374+ {'Something went wrong!'}
375+ </H3>
376+ {errors.value.map(err => (
377+ <p>{err}</p>
378+ ))}
379+ </Warning>
380+ ),
354381 });
355382
356383 const fundingMessage = this.state.fundingMessage.matchWith({
357384 Nothing: () => null,
358− Just: msg => (<Warning className={'mt-8'}>{msg.value}</Warning>)
385+ Just: msg => <Warning className={'mt-8'}>{msg.value}</Warning>,
359386 });
360387
361388 let inputs;
@@ -363,17 +390,15 @@ class InvitesSend extends React.Component {
363390 //TODO proper clickable router thing
364391 inputs = '<- Back to Home';
365392 } else {
366−
367− const inputDisabled = (this.state.status !== STATUS.INPUT);
393+ const inputDisabled = this.state.status !== STATUS.INPUT;
368394
369395 const lessButton = (
370396 <Button
371397 prop-size={'s narrow'}
372398 className={'mt-8'}
373399 disabled={inputDisabled || this.state.targets.length === 1}
374− onClick={this.removeTarget}
375− >
376− { '-' }
400+ onClick={this.removeTarget}>
401+ {'-'}
377402 </Button>
378403 );
379404
@@ -385,12 +410,11 @@ class InvitesSend extends React.Component {
385410 inputDisabled ||
386411 this.state.invitesAvailable.matchWith({
387412 Nothing: () => true,
388− Just: (inv) => (inv.value <= this.state.targets.length)
413+ Just: inv => inv.value <= this.state.targets.length,
389414 })
390415 }
391− onClick={this.addTarget}
392− >
393− { '+' }
416+ onClick={this.addTarget}>
417+ {'+'}
394418 </Button>
395419 );
396420
@@ -399,70 +423,71 @@ class InvitesSend extends React.Component {
399423 const target = this.state.targets[i];
400424 let progress = target.status.matchWith({
401425 Nothing: () => '',
402− Just: status => status.value
426+ Just: status => status.value,
403427 });
404− targetInputs.push(<>
405− <Input
406− value={ target.email }
407− onChange={ (value) => { this.handleEmailInput(i, value); } }
408− placeholder={'Email address'}
409− disabled={ inputDisabled }>
410− </Input>
411− {
412− target.hasReceived.matchWith({
428+ targetInputs.push(
429+ <>
430+ <Input
431+ value={target.email}
432+ onChange={value => {
433+ this.handleEmailInput(i, value);
434+ }}
435+ placeholder={'Email address'}
436+ disabled={inputDisabled}></Input>
437+ {target.hasReceived.matchWith({
413438 Nothing: () => null,
414− Just: (has) => has.value ? <span>{'×'}</span> : <span>{'°'}</span>
415− })
416− }
417− { progress }
418− </>);
439+ Just: has =>
440+ has.value ? <span>{'×'}</span> : <span>{'°'}</span>,
441+ })}
442+ {progress}
443+ </>
444+ );
419445 }
420446
421447 const submitButton = (
422448 <Button
423449 prop-size={'xl wide'}
424450 className={'mt-8'}
425− disabled={ (inputDisabled && (this.state.status !== STATUS.CAN_SEND))
426− || !this.canSend() }
427− onClick={ () => {
428− if (this.state.status === STATUS.INPUT)
429− this.generateInvites();
430− if (this.state.status === STATUS.CAN_SEND)
431− this.sendInvites();
432− }}
433− >
434− { this.state.status }
451+ disabled={
452+ (inputDisabled && this.state.status !== STATUS.CAN_SEND) ||
453+ !this.canSend()
454+ }
455+ onClick={() => {
456+ if (this.state.status === STATUS.INPUT) this.generateInvites();
457+ if (this.state.status === STATUS.CAN_SEND) this.sendInvites();
458+ }}>
459+ {this.state.status}
435460 </Button>
436461 );
437462
438− inputs = (<>
439− { lessButton }
440− { moreButton }
441− { targetInputs }
442− { submitButton }
443− </>);
463+ inputs = (
464+ <>
465+ {lessButton}
466+ {moreButton}
467+ {targetInputs}
468+ {submitButton}
469+ </>
470+ );
444471 }
445472
446473 return (
447474 <Row>
448475 <Col>
476+ <p>{'send invites here, for planets'}</p>
449477
450− <p>{ 'send invites here, for planets' }</p>
478+ <p>{invitesAvailable}</p>
451479
452− <p>{ invitesAvailable }</p>
480+ <ul>{invitesSent}</ul>
453481
454− <ul>{ invitesSent }</ul>
482+ {error}
455483
456− { error }
457−
458− { fundingMessage }
459−
460− { inputs }
484+ {fundingMessage}
461485
486+ {inputs}
462487 </Col>
463488 </Row>
464− )
489+ );
465490 }
466491}
467492
468−export default InvitesSend
493+export default InvitesSend;
src/bridge/views/IssueChild.js+130−128
@@ -1,46 +1,53 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import { azimuth, ecliptic } from 'azimuth-js'
4−import * as ob from 'urbit-ob'
5−import * as need from '../lib/need'
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import { azimuth, ecliptic } from 'azimuth-js';
4+import * as ob from 'urbit-ob';
5+import * as need from '../lib/need';
66
77import {
8− Row, Col, H1, P, Anchor,
9− ShowBlockie, ValidatedSigil,
10− PointInput, AddressInput, InnerLabel
11−} from '../components/Base'
12−
13−import StatelessTransaction from '../components/StatelessTransaction'
14−
15−import { NETWORK_NAMES } from '../lib/network'
16−import { getSpawnCandidate } from '../lib/child'
17−import { canDecodePatp } from '../lib/txn'
18−
19−import { isValidAddress } from '../lib/wallet'
8+ Row,
9+ Col,
10+ H1,
11+ P,
12+ Anchor,
13+ ShowBlockie,
14+ ValidatedSigil,
15+ PointInput,
16+ AddressInput,
17+ InnerLabel,
18+} from '../components/Base';
19+
20+import StatelessTransaction from '../components/StatelessTransaction';
21+
22+import { NETWORK_NAMES } from '../lib/network';
23+import { getSpawnCandidate } from '../lib/child';
24+import { canDecodePatp } from '../lib/txn';
25+
26+import { isValidAddress } from '../lib/wallet';
2027
2128const setFind = (set, pred) => {
2229 for (const e of set) {
2330 if (pred(e)) {
24− return e
31+ return e;
2532 }
2633 }
27− return undefined
28−}
34+ return undefined;
35+};
2936
3037class IssueChild extends React.Component {
3138 constructor(props) {
32− super(props)
39+ super(props);
3340
3441 const issuingPoint = parseInt(need.pointCursor(props), 10);
3542
36− const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint))
43+ const getCandidate = () => ob.patp(getSpawnCandidate(issuingPoint));
3744
3845 const suggestions = [
3946 getCandidate(),
4047 getCandidate(),
4148 getCandidate(),
42− getCandidate()
43− ]
49+ getCandidate(),
50+ ];
4451
4552 this.state = {
4653 receivingAddress: '',
@@ -49,193 +56,187 @@ class IssueChild extends React.Component {
4956 isAvailable: Nothing(), // use Nothing to allow attempt when offline
5057 suggestions: suggestions,
5158 validChildren: new Set(),
52− autoComplete: ''
53− }
59+ autoComplete: '',
60+ };
5461
55− this.handlePointInput = this.handlePointInput.bind(this)
56− this.handleAddressInput = this.handleAddressInput.bind(this)
57− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
62+ this.handlePointInput = this.handlePointInput.bind(this);
63+ this.handleAddressInput = this.handleAddressInput.bind(this);
64+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
5865 this.statelessRef = React.createRef();
5966 }
6067
61− handleAddressInput = (receivingAddress) => {
62− this.setState({ receivingAddress })
63− this.statelessRef.current.clearTxn()
64− }
68+ handleAddressInput = receivingAddress => {
69+ this.setState({ receivingAddress });
70+ this.statelessRef.current.clearTxn();
71+ };
6572
6673 componentWillMount() {
67− const { issuingPoint } = this.state
74+ const { issuingPoint } = this.state;
6875
6976 const validContracts = need.contracts(this.props);
7077
71− azimuth.getUnspawnedChildren(validContracts, issuingPoint).then(ps => this.setState({ validChildren: new Set(ps.map(ob.patp)) }))
78+ azimuth
79+ .getUnspawnedChildren(validContracts, issuingPoint)
80+ .then(ps => this.setState({ validChildren: new Set(ps.map(ob.patp)) }));
7281 }
7382
7483 handlePointInput(desiredPoint) {
7584 if (desiredPoint.length < 15) {
85+ const suggestedPoint =
86+ setFind(this.state.validChildren, e => e.startsWith(desiredPoint)) ||
87+ '';
88+ const rendered =
89+ ' '.repeat(desiredPoint.length) +
90+ suggestedPoint.substring(desiredPoint.length);
7691
77− const suggestedPoint = setFind(this.state.validChildren, (e => e.startsWith(desiredPoint))) || ''
78− const rendered = ' '.repeat(desiredPoint.length) + suggestedPoint.substring(desiredPoint.length)
79−
80− const available = Just(this.state.validChildren.has(desiredPoint))
92+ const available = Just(this.state.validChildren.has(desiredPoint));
8193
8294 this.setState({
8395 desiredPoint,
8496 isAvailable: available,
85− autoComplete: rendered
86− })
87− this.statelessRef.current.clearTxn()
88−
97+ autoComplete: rendered,
98+ });
99+ this.statelessRef.current.clearTxn();
89100 }
90101 }
91102
92−
93103 createUnsignedTxn() {
94− const { state, props } = this
104+ const { state, props } = this;
95105
96− if (isValidAddress(state.receivingAddress) === false) return Nothing()
97− if (state.isAvailable === false) return Nothing()
98− if (canDecodePatp(state.desiredPoint) === false) return Nothing()
106+ if (isValidAddress(state.receivingAddress) === false) return Nothing();
107+ if (state.isAvailable === false) return Nothing();
108+ if (canDecodePatp(state.desiredPoint) === false) return Nothing();
99109
100110 const validContracts = need.contracts(props);
101111
102− const pointDec = ob.patp2dec(state.desiredPoint)
112+ const pointDec = ob.patp2dec(state.desiredPoint);
103113
104114 const txn = ecliptic.spawn(
105115 validContracts,
106116 pointDec,
107117 state.receivingAddress
108− )
118+ );
109119
110− return Just(txn)
120+ return Just(txn);
111121 }
112122
113−
114123 validatePoint = patp => {
115− const point = this.point
124+ const point = this.point;
116125
117− let vpatp = false
126+ let vpatp = false;
118127 try {
119− vpatp = ob.isValidPatp(patp)
120− } catch(_) {
121− }
128+ vpatp = ob.isValidPatp(patp);
129+ } catch (_) {}
122130
123− let vchild = false
131+ let vchild = false;
124132 try {
125− vchild = ob.sein(patp) === ob.patp(point)
126− } catch(_) {
127− }
133+ vchild = ob.sein(patp) === ob.patp(point);
134+ } catch (_) {}
128135
129− return vpatp && vchild
130− }
136+ return vpatp && vchild;
137+ };
131138
132139 render() {
133− const { props, state } = this
140+ const { props, state } = this;
134141
135− const validAddress = isValidAddress(state.receivingAddress)
142+ const validAddress = isValidAddress(state.receivingAddress);
136143
137144 const canGenerate = props.web3.matchWith({
138145 Nothing: () => {
139− return validAddress === true
146+ return validAddress === true;
140147 },
141148 Just: () => {
142− return validAddress === true &&
143− state.isAvailable.value === true
144− }
145− })
149+ return validAddress === true && state.isAvailable.value === true;
150+ },
151+ });
146152
147153 const esvisible =
148− props.networkType === NETWORK_NAMES.ROPSTEN ||
149− props.networkType === NETWORK_NAMES.MAINNET
154+ props.networkType === NETWORK_NAMES.ROPSTEN ||
155+ props.networkType === NETWORK_NAMES.MAINNET;
150156
151157 const esdomain =
152− props.networkType === NETWORK_NAMES.ROPSTEN
153− ? "ropsten.etherscan.io"
154− : "etherscan.io"
158+ props.networkType === NETWORK_NAMES.ROPSTEN
159+ ? 'ropsten.etherscan.io'
160+ : 'etherscan.io';
155161
156162 return (
157163 <Row>
158164 <Col>
159165 <H1>
160− { 'Issue a Child From ' } <code>{ `${ob.patp(state.issuingPoint)}` }</code>
166+ {'Issue a Child From '}{' '}
167+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
161168 </H1>
162169
163−
164170 <P>
165− {
166− `Please enter the point you would like to issue, and specify the
171+ {`Please enter the point you would like to issue, and specify the
167172 receiving Ethereum address. If you need to create an address, you
168− can also use Wallet Generator.`
169− }
173+ can also use Wallet Generator.`}
170174 </P>
171175
172176 <P>
173− {
174− `Your point can only issue children with particular names. Some
175− valid suggestions for `
176− }
177− {
178− <code>
179− { ob.patp(state.issuingPoint) }
180− </code>
181− }
182− {
183− ' are '
184− }
185− <code>
186− { state.suggestions[0] }
187− </code>
188− { ', ' }
189− <code>
190− { state.suggestions[1] }
191− </code>
192− { ', and ' }
193− <code>
194− { state.suggestions[2] }
195− </code>
196− { '.' }
177+ {`Your point can only issue children with particular names. Some
178+ valid suggestions for `}
179+ {<code>{ob.patp(state.issuingPoint)}</code>}
180+ {' are '}
181+ <code>{state.suggestions[0]}</code>
182+ {', '}
183+ <code>{state.suggestions[1]}</code>
184+ {', and '}
185+ <code>{state.suggestions[2]}</code>
186+ {'.'}
197187 </P>
198188
199189 <PointInput
200− prop-size='lg'
201− prop-format='innerLabel'
190+ prop-size="lg"
191+ prop-format="innerLabel"
202192 className={'mono mt-8'}
203− placeholder={ `e.g. ${state.suggestions[3]}` }
204− value={ state.desiredPoint }
205− onChange={ this.handlePointInput }>
206− <InnerLabel>{ 'Point to Issue' }</InnerLabel>
193+ placeholder={`e.g. ${state.suggestions[3]}`}
194+ value={state.desiredPoint}
195+ onChange={this.handlePointInput}>
196+ <InnerLabel>{'Point to Issue'}</InnerLabel>
207197 <div
208− style={{marginTop: '8.8rem', marginLeft: '4.6rem', fontSize: '5rem', color: '#757575', whiteSpace: 'pre-wrap'}}
209− className='abs tl-0 mono'>
210− { state.autoComplete }
198+ style={{
199+ marginTop: '8.8rem',
200+ marginLeft: '4.6rem',
201+ fontSize: '5rem',
202+ color: '#757575',
203+ whiteSpace: 'pre-wrap',
204+ }}
205+ className="abs tl-0 mono">
206+ {state.autoComplete}
211207 </div>
212208 <ValidatedSigil
213209 className={'tr-0 mt-05 mr-0 abs'}
214210 patp={state.desiredPoint}
215211 size={68}
216212 margin={8}
217− validator={() => this.validatePoint(state.desiredPoint)} />
213+ validator={() => this.validatePoint(state.desiredPoint)}
214+ />
218215 </PointInput>
219216
220217 <AddressInput
221− className='text-mono mt-8'
222− prop-size='lg'
223− prop-format='innerLabel'
224− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
225− value={ state.receivingAddress }
226− disabled={ Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value }
227− onChange={ v => this.handleAddressInput(v) }>
228− <InnerLabel>{ 'Receiving Address' }</InnerLabel>
218+ className="text-mono mt-8"
219+ prop-size="lg"
220+ prop-format="innerLabel"
221+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
222+ value={state.receivingAddress}
223+ disabled={
224+ Nothing.hasInstance(state.isAvailable) || !state.isAvailable.value
225+ }
226+ onChange={v => this.handleAddressInput(v)}>
227+ <InnerLabel>{'Receiving Address'}</InnerLabel>
229228 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
230229 </AddressInput>
231230
232231 <Anchor
233232 className={'mt-1'}
234233 prop-size={'sm'}
235− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
234+ prop-disabled={
235+ !isValidAddress(state.receivingAddress) || !esvisible
236+ }
236237 target={'_blank'}
237238 href={`https://${esdomain}/address/${state.receivingAddress}`}>
238− {'View on Etherscan ↗'}
239+ {'View on Etherscan ↗'}
239240 </Anchor>
240241
241242 <StatelessTransaction
@@ -244,11 +245,12 @@ class IssueChild extends React.Component {
244245 // Other
245246 canGenerate={canGenerate}
246247 createUnsignedTxn={this.createUnsignedTxn}
247− ref={this.statelessRef} />
248+ ref={this.statelessRef}
249+ />
248250 </Col>
249251 </Row>
250− )
252+ );
251253 }
252254}
253255
254−export default IssueChild
256+export default IssueChild;
src/bridge/views/Keystore.js+104−108
@@ -1,168 +1,164 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import { Ok, Error } from 'folktale/result'
3−import React from 'react'
4−import * as keythereum from 'keythereum'
5−import { Button, UploadButton } from '../components/Base'
6−import { Input, InnerLabel, InputCaption } from '../components/Base'
7−import { Row, Col, H1, H3, Warning } from '../components/Base'
8−
9−import { BRIDGE_ERROR } from '../lib/error'
10−import { ROUTE_NAMES } from '../lib/router'
11−import { EthereumWallet } from '../lib/wallet'
1+import { Just, Nothing } from 'folktale/maybe';
2+import { Ok, Error } from 'folktale/result';
3+import React from 'react';
4+import * as keythereum from 'keythereum';
5+import { Button, UploadButton } from '../components/Base';
6+import { Input, InnerLabel, InputCaption } from '../components/Base';
7+import { Row, Col, H1, H3, Warning } from '../components/Base';
8+
9+import { BRIDGE_ERROR } from '../lib/error';
10+import { ROUTE_NAMES } from '../lib/router';
11+import { EthereumWallet } from '../lib/wallet';
1212
1313class Keystore extends React.Component {
14−
1514 constructor(props) {
16− super(props)
15+ super(props);
1716
1817 this.state = {
1918 keystore: Nothing(), // Maybe<Result<String, String>>
2019 password: '',
21− decryptionProblem: false
22− }
20+ decryptionProblem: false,
21+ };
2322
24− this.handleKeystoreUpload = this.handleKeystoreUpload.bind(this)
25− this.handlePasswordInput = this.handlePasswordInput.bind(this)
26− this.constructWallet = this.constructWallet.bind(this)
23+ this.handleKeystoreUpload = this.handleKeystoreUpload.bind(this);
24+ this.handlePasswordInput = this.handlePasswordInput.bind(this);
25+ this.constructWallet = this.constructWallet.bind(this);
2726 }
2827
2928 handlePasswordInput(password) {
30− this.setState({ password })
29+ this.setState({ password });
3130 }
3231
3332 constructWallet() {
34− const { setWallet } = this.props
35− const { keystore, password } = this.state
33+ const { setWallet } = this.props;
34+ const { keystore, password } = this.state;
3635
3736 try {
38−
3937 const text = keystore.matchWith({
40− Nothing: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE },
41− Just: ks => ks.value.matchWith({
42− Ok: result => result.value,
43− Error: _ => { throw BRIDGE_ERROR.MISSING_KEYSTORE }
44− })
45− })
46−
47− const json = JSON.parse(text)
48− const privateKey = keythereum.recover(password, json)
49−
50− const wallet = new EthereumWallet(privateKey)
51− this.setState({ decryptionProblem: false })
52− setWallet(Just(wallet))
53−
38+ Nothing: _ => {
39+ throw BRIDGE_ERROR.MISSING_KEYSTORE;
40+ },
41+ Just: ks =>
42+ ks.value.matchWith({
43+ Ok: result => result.value,
44+ Error: _ => {
45+ throw BRIDGE_ERROR.MISSING_KEYSTORE;
46+ },
47+ }),
48+ });
49+
50+ const json = JSON.parse(text);
51+ const privateKey = keythereum.recover(password, json);
52+
53+ const wallet = new EthereumWallet(privateKey);
54+ this.setState({ decryptionProblem: false });
55+ setWallet(Just(wallet));
5456 } catch (err) {
55−
56− this.setState({ decryptionProblem: true })
57− setWallet(Nothing())
58−
57+ this.setState({ decryptionProblem: true });
58+ setWallet(Nothing());
5959 }
6060 }
6161
62− handleKeystoreUpload = (event) => {
63− const file = event.files.item(0)
64− const reader = new FileReader()
62+ handleKeystoreUpload = event => {
63+ const file = event.files.item(0);
64+ const reader = new FileReader();
6565
66− reader.onload = (e) => {
67− const keystore = e.target.result
68− this.setState({ keystore: Just(Ok(keystore)) })
69− }
66+ reader.onload = e => {
67+ const keystore = e.target.result;
68+ this.setState({ keystore: Just(Ok(keystore)) });
69+ };
7070
7171 const failure = _ => {
72− const message = 'There was a problem uploading your Keystore file'
73− this.setState({ keystore: Just(Error(message)) })
74− }
72+ const message = 'There was a problem uploading your Keystore file';
73+ this.setState({ keystore: Just(Error(message)) });
74+ };
7575
76− reader.onerror = failure
77− reader.onabort = failure
76+ reader.onerror = failure;
77+ reader.onabort = failure;
7878
79− reader.readAsText(file)
80− }
79+ reader.readAsText(file);
80+ };
8181
8282 render() {
83− const { pushRoute, popRoute, wallet } = this.props
84− const { keystore, password, decryptionProblem } = this.state
83+ const { pushRoute, popRoute, wallet } = this.props;
84+ const { keystore, password, decryptionProblem } = this.state;
8585
8686 const uploadButtonClass = keystore.matchWith({
8787 Nothing: _ => 'bg-blue white',
88− Just: ks => ks.value.matchWith({
89− Ok: _ => 'bg-green white',
90− Error: _ => 'bg-yellow black'
91− })
92− })
88+ Just: ks =>
89+ ks.value.matchWith({
90+ Ok: _ => 'bg-green white',
91+ Error: _ => 'bg-yellow black',
92+ }),
93+ });
9394
9495 const decryptMessage =
95− decryptionProblem === false
96− ? <div />
97− : <Warning className={'mt-8'}>
98− <H3 style={{marginTop: 0, paddingTop: 0}}>
99− {
100− "Couldn't decrypt wallet."
101− }
96+ decryptionProblem === false ? (
97+ <div />
98+ ) : (
99+ <Warning className={'mt-8'}>
100+ <H3 style={{ marginTop: 0, paddingTop: 0 }}>
101+ {"Couldn't decrypt wallet."}
102102 </H3>
103− { 'You may have entered an incorrect password.' }
103+ {'You may have entered an incorrect password.'}
104104 </Warning>
105+ );
105106
106107 return (
107− <Row>
108− <Col className={'measure-md'}>
109− <H1 className={'mb-4'}>{ 'Upload Your Keystore File' }</H1>
108+ <Row>
109+ <Col className={'measure-md'}>
110+ <H1 className={'mb-4'}>{'Upload Your Keystore File'}</H1>
110111 <InputCaption>
111− { `Please upload your Ethereum keystore file. If your keystore
112+ {`Please upload your Ethereum keystore file. If your keystore
112113 file is encrypted with a password, you'll also need to enter
113− that below.` }
114+ that below.`}
114115 </InputCaption>
115116
116117 <UploadButton
117− className={ `btn ${uploadButtonClass} mt-10` }
118− onChange={ this.handleKeystoreUpload }
119− >
120− <div className={'flex-center-all fs-4 h-11 pointer'}>{ 'Upload Keystore file' }</div>
118+ className={`btn ${uploadButtonClass} mt-10`}
119+ onChange={this.handleKeystoreUpload}>
120+ <div className={'flex-center-all fs-4 h-11 pointer'}>
121+ {'Upload Keystore file'}
122+ </div>
121123 </UploadButton>
122124
123125 <Input
124− className='pt-8 mt-8'
125− prop-size='md'
126− prop-format='innerLabel'
127− type='password'
128− name='password'
129− onChange={ this.handlePasswordInput }
130− value={ password }
131− autocomplete='off'
126+ className="pt-8 mt-8"
127+ prop-size="md"
128+ prop-format="innerLabel"
129+ type="password"
130+ name="password"
131+ onChange={this.handlePasswordInput}
132+ value={password}
133+ autocomplete="off"
132134 autoFocus>
133135 <InnerLabel>{'Password'}</InnerLabel>
134136 </Input>
135137
136138 <Button
137139 className={` mt-10`}
138− prop-size={ 'wide lg' }
139− disabled={ Nothing.hasInstance(keystore) }
140− onClick={ this.constructWallet }
141− >
142− { 'Decrypt' }
140+ prop-size={'wide lg'}
141+ disabled={Nothing.hasInstance(keystore)}
142+ onClick={this.constructWallet}>
143+ {'Decrypt'}
143144 </Button>
144145
145− { decryptMessage }
146+ {decryptMessage}
146147
147148 <Button
148149 className={'mt-10'}
149− prop-size={ 'wide lg' }
150− disabled={ Nothing.hasInstance(wallet) }
151− onClick={ () => {
152− popRoute()
153− pushRoute(ROUTE_NAMES.SHIPS)
154− }
155− }
156− >
157− { 'Continue →' }
150+ prop-size={'wide lg'}
151+ disabled={Nothing.hasInstance(wallet)}
152+ onClick={() => {
153+ popRoute();
154+ pushRoute(ROUTE_NAMES.SHIPS);
155+ }}>
156+ {'Continue →'}
158157 </Button>
159−
160− </Col>
161− </Row>
162−
163−
164− )
158+ </Col>
159+ </Row>
160+ );
165161 }
166162}
167163
168−export default Keystore
164+export default Keystore;
src/bridge/views/Landing.js+50−60
@@ -1,68 +1,57 @@
1−import React from 'react'
2−import { Button } from '../components/Base'
3−import { Row, Col } from '../components/Base'
4−import { H1, P } from '../components/Base'
1+import React from 'react';
2+import { Button } from '../components/Base';
3+import { Row, Col } from '../components/Base';
4+import { H1, P } from '../components/Base';
55
6−import { ROUTE_NAMES } from '../lib/router'
6+import { ROUTE_NAMES } from '../lib/router';
77
8−const Landing = (props) =>
9− <Row>
10− <Col>
11− <H1>{ 'Welcome' }</H1>
8+const Landing = props => (
9+ <Row>
10+ <Col>
11+ <H1>{'Welcome'}</H1>
1212
13+ <P>
14+ {'Bridge is a tool for managing and viewing assets on ' +
15+ 'Azimuth, the Urbit address space.'}
16+ </P>
1317
14− <P>
15− { 'Bridge is a tool for managing and viewing assets on ' +
16− 'Azimuth, the Urbit address space.' }
17− </P>
18+ <Button
19+ prop-type="link"
20+ prop-size="lg"
21+ className={'mb-4 mt-8'}
22+ onClick={() => props.pushRoute(ROUTE_NAMES.INVITE_TICKET)}>
23+ {'Claim an invite →'}
24+ </Button>
1825
26+ <P>{'If you were sent an Azimuth invite code, start here.'}</P>
1927
28+ <Button
29+ prop-type="link"
30+ prop-size="lg"
31+ className={'mb-4 mt-8'}
32+ onClick={() => props.pushRoute(ROUTE_NAMES.NETWORK)}>
33+ {'Unlock a Wallet →'}
34+ </Button>
2035
21− <Button
22− prop-type='link'
23− prop-size='lg'
24− className={'mb-4 mt-8'}
25− onClick={ () => props.pushRoute(ROUTE_NAMES.INVITE_TICKET) }
26− >
27− { 'Claim an invite →' }
28− </Button>
29−
30− <P>
31− { "If you were sent an Azimuth invite code, start here." }
32− </P>
33−
34−
35− <Button
36− prop-type='link'
37− prop-size='lg'
38− className={'mb-4 mt-8'}
39− onClick={ () => props.pushRoute(ROUTE_NAMES.NETWORK) }
40− >
41− { 'Unlock a Wallet →' }
42− </Button>
43−
44− <P>
45− { "If you own Azimuth assets and want to manage them in some " +
36+ <P>
37+ {'If you own Azimuth assets and want to manage them in some ' +
4638 "way, start here. You'll need either your Urbit ticket or a " +
47− "keypair." }
48− </P>
39+ 'keypair.'}
40+ </P>
4941
50− {
51− <Row>
52− <Col>
53− <Button
54− prop-type={'link'}
55− prop-size={'lg'}
56− className={'mb-4 mt-8'}
57− onClick={ () => props.pushRoute(ROUTE_NAMES.VIEW_SHIP) }
58− >
59− { 'View a point →' }
60− </Button>
61− <P>
62− { "View an Azimuth point without signing into a wallet." }
63− </P>
64− </Col>
65− </Row>
42+ {
43+ <Row>
44+ <Col>
45+ <Button
46+ prop-type={'link'}
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>
6655 // FIXME add address view
6756 // <LinkButton
6857 // size={'l'}
@@ -75,8 +64,9 @@ const Landing = (props) =>
7564 // <p>
7665 // { "Lorem" }
7766 // </p>
78− }
79− </Col>
80− </Row>
67+ }
68+ </Col>
69+ </Row>
70+);
8171
82−export default Landing
72+export default Landing;
src/bridge/views/Ledger.js+144−135
@@ -1,38 +1,32 @@
1−import * as bip32 from 'bip32'
2−import React from 'react'
3−import Maybe from 'folktale/maybe'
4−import { Button } from '../components/Base'
5−import { Row, Col, H1, P, H2 } from '../components/Base'
6−import { Input, InnerLabel, InnerLabelDropdown } from '../components/Base'
7−import Transport from '@ledgerhq/hw-transport-u2f'
8−import Eth from '@ledgerhq/hw-app-eth'
9−import * as secp256k1 from 'secp256k1'
10−
11−import { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH } from '../lib/ledger'
12−import { ROUTE_NAMES } from '../lib/router'
13−
14−const chopHdPrefix = str =>
15− str.slice(0, 2) === "m/"
16− ? str.slice(2)
17− : str
18−
19−const addHdPrefix = str =>
20− str.slice(0, 2) === "m/"
21− ? str
22− : "m/" + str
1+import * as bip32 from 'bip32';
2+import React from 'react';
3+import Maybe from 'folktale/maybe';
4+import { Button } from '../components/Base';
5+import { Row, Col, H1, P, H2 } from '../components/Base';
6+import { Input, InnerLabel, InnerLabelDropdown } from '../components/Base';
7+import Transport from '@ledgerhq/hw-transport-u2f';
8+import Eth from '@ledgerhq/hw-app-eth';
9+import * as secp256k1 from 'secp256k1';
10+
11+import { LEDGER_LIVE_PATH, LEDGER_LEGACY_PATH } from '../lib/ledger';
12+import { ROUTE_NAMES } from '../lib/router';
13+
14+const chopHdPrefix = str => (str.slice(0, 2) === 'm/' ? str.slice(2) : str);
15+
16+const addHdPrefix = str => (str.slice(0, 2) === 'm/' ? str : 'm/' + str);
2317
2418class Ledger extends React.Component {
2519 constructor(props) {
26− super(props)
20+ super(props);
2721
2822 this.state = {
2923 basePath: LEDGER_LIVE_PATH,
3024 hdpath: LEDGER_LIVE_PATH.replace(/x/g, 0),
31− account: 0
32− }
25+ account: 0,
26+ };
3327
34− this.pollDevice = this.pollDevice.bind(this)
35− this.handleHdPathInput = this.handleHdPathInput.bind(this)
28+ this.pollDevice = this.pollDevice.bind(this);
29+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
3630 this.handlePathSelection = this.handlePathSelection.bind(this);
3731 this.handleAccountSelection = this.handleAccountSelection.bind(this);
3832 this.updateHdPath = this.updateHdPath.bind(this);
@@ -55,157 +49,172 @@ class Ledger extends React.Component {
5549 }
5650
5751 handleHdPathInput(hdpath) {
58− this.setState({ hdpath })
52+ this.setState({ hdpath });
5953 }
6054
6155 async pollDevice() {
62− const { setWallet, setWalletHdPath } = this.props
63− const { hdpath } = this.state
64−
65− const transport = await Transport.create()
66− const eth = new Eth(transport)
67− const path = chopHdPrefix(hdpath)
68−
69− eth.getAddress(path, false, true)
70− .then(info => {
71− const publicKey = Buffer.from(info.publicKey, 'hex')
72− const chainCode = Buffer.from(info.chainCode, 'hex')
73− const pub = secp256k1.publicKeyConvert(publicKey, true)
74− const hd = bip32.fromPublicKey(pub, chainCode)
75− setWallet(Maybe.Just(hd))
76− setWalletHdPath(addHdPrefix(hdpath))
77− }, _ => {
78− setWallet(Maybe.Nothing())
79− })
56+ const { setWallet, setWalletHdPath } = this.props;
57+ const { hdpath } = this.state;
58+
59+ const transport = await Transport.create();
60+ const eth = new Eth(transport);
61+ const path = chopHdPrefix(hdpath);
62+
63+ eth.getAddress(path, false, true).then(
64+ info => {
65+ const publicKey = Buffer.from(info.publicKey, 'hex');
66+ const chainCode = Buffer.from(info.chainCode, 'hex');
67+ const pub = secp256k1.publicKeyConvert(publicKey, true);
68+ const hd = bip32.fromPublicKey(pub, chainCode);
69+ setWallet(Maybe.Just(hd));
70+ setWalletHdPath(addHdPrefix(hdpath));
71+ },
72+ _ => {
73+ setWallet(Maybe.Nothing());
74+ }
75+ );
8076 }
8177
8278 render() {
83− const { pushRoute, popRoute, wallet } = this.props
79+ const { pushRoute, popRoute, wallet } = this.props;
8480 const { basePath, hdpath, account } = this.state;
8581 const { handlePathSelection, handleAccountSelection } = this;
8682
8783 const pathOptions = [
88− { title: 'Ledger Live',
89− value: LEDGER_LIVE_PATH
90− },
91− { title: 'Ledger Legacy',
92− value: LEDGER_LEGACY_PATH
93− },
84+ { title: 'Ledger Live', value: LEDGER_LIVE_PATH },
85+ { title: 'Ledger Legacy', value: LEDGER_LEGACY_PATH },
9486 {
9587 title: 'Custom path',
96− value: 'custom'
97− }
88+ value: 'custom',
89+ },
9890 ];
99− let basePathTitle = pathOptions.find(o => (o.value === basePath)).title;
91+ let basePathTitle = pathOptions.find(o => o.value === basePath).title;
10092
10193 let accountOptions = [];
10294 for (let i = 0; i < 20; i++) {
10395 accountOptions.push({
104− title: 'Account #' + (i+1),
105− value: i
96+ title: 'Account #' + (i + 1),
97+ value: i,
10698 });
10799 }
108− let accountTitle = accountOptions.find(o => (o.value === account)).title;
100+ let accountTitle = accountOptions.find(o => o.value === account).title;
109101
110102 const basePathSelection = (
111103 <InnerLabelDropdown
112− className='mt-8'
104+ className="mt-8"
113105 options={pathOptions}
114106 handleUpdate={handlePathSelection}
115107 title={'Derivation path'}
116108 currentSelectionTitle={basePathTitle}
117− fullWidth={true}
118− >
119− </InnerLabelDropdown>
109+ fullWidth={true}></InnerLabelDropdown>
120110 );
121111
122− const truePathSelection = (basePath === 'custom')
123− ? ( <Input
124− className='pt-8 mt-4 text-mono'
125− prop-size='md'
126− prop-format='innerLabel'
127− name='hdpath'
128− value={ addHdPrefix(hdpath) }
129− autocomplete='off'
130− onChange={ this.handleHdPathInput }>
131− <InnerLabel>{'HD Path'}</InnerLabel>
132− </Input> )
133− : ( <InnerLabelDropdown
134− className='mt-4'
135− prop-size='md'
136− prop-format='innerLabel'
137− options={accountOptions}
138− handleUpdate={handleAccountSelection}
139− title={'Account'}
140− currentSelectionTitle={accountTitle}
141− fullWidth={true}
142− >
143− </InnerLabelDropdown> );
112+ const truePathSelection =
113+ basePath === 'custom' ? (
114+ <Input
115+ className="pt-8 mt-4 text-mono"
116+ prop-size="md"
117+ prop-format="innerLabel"
118+ name="hdpath"
119+ value={addHdPrefix(hdpath)}
120+ autocomplete="off"
121+ onChange={this.handleHdPathInput}>
122+ <InnerLabel>{'HD Path'}</InnerLabel>
123+ </Input>
124+ ) : (
125+ <InnerLabelDropdown
126+ className="mt-4"
127+ prop-size="md"
128+ prop-format="innerLabel"
129+ options={accountOptions}
130+ handleUpdate={handleAccountSelection}
131+ title={'Account'}
132+ currentSelectionTitle={accountTitle}
133+ fullWidth={true}></InnerLabelDropdown>
134+ );
144135
145136 return (
137+ <Row>
138+ <Col className={'measure-md'}>
139+ <H1>{'Authenticate With Your Ledger'}</H1>
146140
147− <Row>
148− <Col className={'measure-md'}>
149− <H1>{ 'Authenticate With Your Ledger' }</H1>
141+ <H2>{'Running on HTTPS?'}</H2>
150142
151− <H2>{'Running on HTTPS?'}</H2>
152−
153− <P>
154− { `Connect and authenticate to your Ledger, and then open the
143+ <P>
144+ {`Connect and authenticate to your Ledger, and then open the
155145 "Ethereum" application. If you're running on older firmware,
156146 make sure the "browser support" option is turned on. To sign
157147 transactions, you'll also need to enable the "contract data"
158− option.` }
159− </P>
148+ option.`}
149+ </P>
160150
161− <P>
162− { `If you'd like to use a custom derivation path, you may enter
163− it below.`
164− }
165− </P>
151+ <P>
152+ {`If you'd like to use a custom derivation path, you may enter
153+ it below.`}
154+ </P>
166155
167− <H2>{'Running on HTTP?'}</H2>
156+ <H2>{'Running on HTTP?'}</H2>
168157
169− <P>{`To authenticate and sign transactions with a Ledger, Bridge
158+ <P>{`To authenticate and sign transactions with a Ledger, Bridge
170159 must be serving over HTTPS on localhost. You can do this via the
171160 following:`}</P>
172161
173− <ol className={'measure-md'}>
174− <li className={'mt-4'}>{'Install'} <a target={'_blank'} href={'https://github.com/FiloSottile/mkcert'}>{'mkcert'}</a></li>
175− <li className={'mt-4'}>{'Install a local certificate authority via '}<code>{'mkcert -install'}</code></li>
176− <li className={'mt-4'}>{'In your '}<code>{'bridge'}</code>{' directory, generate a certificate valid for localhost via '} <code>{'mkcert localhost'}</code>{'.'}
177− {'This will produce two files: '}<code>{'localhost.pem'}</code> {', the local certificate, and '}<code>{'localhost-key.pem'}</code>{', its corresponding private key.'}
178− </li>
179− <li className={'mt-4'}>{'Run '}<code>{'python bridge-https.py'}</code></li>
180− </ol>
181−
182− { basePathSelection }
183− { truePathSelection }
184−
185− <Button
186− prop-size={'wide lg'}
187− className={'mt-8'}
188− onClick={ this.pollDevice }>
189− { 'Authenticate →' }
190− </Button>
191−
192− <Button
193− className={'mt-8'}
194− prop-size={'wide lg'}
195− disabled={ Maybe.Nothing.hasInstance(wallet) }
196− onClick={
197− () => {
198− popRoute()
199− pushRoute(ROUTE_NAMES.SHIPS)
200− }
201− }>
202− { 'Continue →' }
203− </Button>
204−
162+ <ol className={'measure-md'}>
163+ <li className={'mt-4'}>
164+ {'Install'}{' '}
165+ <a
166+ target={'_blank'}
167+ href={'https://github.com/FiloSottile/mkcert'}>
168+ {'mkcert'}
169+ </a>
170+ </li>
171+ <li className={'mt-4'}>
172+ {'Install a local certificate authority via '}
173+ <code>{'mkcert -install'}</code>
174+ </li>
175+ <li className={'mt-4'}>
176+ {'In your '}
177+ <code>{'bridge'}</code>
178+ {
179+ ' directory, generate a certificate valid for localhost via '
180+ }{' '}
181+ <code>{'mkcert localhost'}</code>
182+ {'.'}
183+ {'This will produce two files: '}
184+ <code>{'localhost.pem'}</code> {', the local certificate, and '}
185+ <code>{'localhost-key.pem'}</code>
186+ {', its corresponding private key.'}
187+ </li>
188+ <li className={'mt-4'}>
189+ {'Run '}
190+ <code>{'python bridge-https.py'}</code>
191+ </li>
192+ </ol>
193+
194+ {basePathSelection}
195+ {truePathSelection}
196+
197+ <Button
198+ prop-size={'wide lg'}
199+ className={'mt-8'}
200+ onClick={this.pollDevice}>
201+ {'Authenticate →'}
202+ </Button>
203+
204+ <Button
205+ className={'mt-8'}
206+ prop-size={'wide lg'}
207+ disabled={Maybe.Nothing.hasInstance(wallet)}
208+ onClick={() => {
209+ popRoute();
210+ pushRoute(ROUTE_NAMES.SHIPS);
211+ }}>
212+ {'Continue →'}
213+ </Button>
205214 </Col>
206215 </Row>
207− )
216+ );
208217 }
209218}
210219
211−export default Ledger
220+export default Ledger;
src/bridge/views/Mnemonic.js+105−116
@@ -1,176 +1,165 @@
1−import * as bip39 from 'bip39'
2−import { Just, Nothing } from 'folktale/maybe'
3−import React from 'react'
4−import { Button } from '../components/Base'
1+import * as bip39 from 'bip39';
2+import { Just, Nothing } from 'folktale/maybe';
3+import React from 'react';
4+import { Button } from '../components/Base';
55import {
66 Input,
77 MnemonicInput,
88 InnerLabel,
9− InputCaption
10− } from '../components/Base'
11−import { Row, Col, H1 } from '../components/Base'
12−
13−import { ROUTE_NAMES } from '../lib/router'
14−import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet'
9+ InputCaption,
10+} from '../components/Base';
11+import { Row, Col, H1 } from '../components/Base';
1512
13+import { ROUTE_NAMES } from '../lib/router';
14+import { DEFAULT_HD_PATH, walletFromMnemonic } from '../lib/wallet';
1615
1716class Mnemonic extends React.Component {
18−
1917 constructor(props) {
20− super(props)
18+ super(props);
2119
2220 this.state = {
2321 mnemonic: '',
2422 passphrase: '',
2523 hdpath: DEFAULT_HD_PATH,
26− }
24+ };
2725
28− this.handleMnemonicInput = this.handleMnemonicInput.bind(this)
29− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
30− this.handleHdPathInput = this.handleHdPathInput.bind(this)
26+ this.handleMnemonicInput = this.handleMnemonicInput.bind(this);
27+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
28+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
3129 }
3230
3331 componentDidMount() {
34− const { mnemonic, hdpath, passphrase } = this.state
35− this.attemptWalletDerivation(mnemonic, hdpath, passphrase)
32+ const { mnemonic, hdpath, passphrase } = this.state;
33+ this.attemptWalletDerivation(mnemonic, hdpath, passphrase);
3634
3735 this.setState({
3836 exampleMnemonic: bip39.generateMnemonic(),
39− })
37+ });
4038 }
4139
4240 handlePassphraseInput(passphrase) {
4341 this.setState((state, _) => {
44− const mnemonic = state.mnemonic
45− const hdpath = state.hdpath
42+ const mnemonic = state.mnemonic;
43+ const hdpath = state.hdpath;
4644 this.attemptWalletDerivation(
4745 mnemonic,
4846 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
4947 passphrase
50− )
48+ );
5149 return {
52− passphrase
53− }
54− })
50+ passphrase,
51+ };
52+ });
5553 }
5654
5755 handleMnemonicInput(mnemonic) {
5856 this.setState((state, _) => {
59− const hdpath = state.hdpath
60− const passphrase = state.passphrase
57+ const hdpath = state.hdpath;
58+ const passphrase = state.passphrase;
6159 this.attemptWalletDerivation(
6260 mnemonic,
6361 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
6462 passphrase
65− )
63+ );
6664 return {
67− mnemonic
68− }
69− })
65+ mnemonic,
66+ };
67+ });
7068 }
7169
7270 handleHdPathInput(hdpath) {
7371 this.setState((state, _) => {
74− const mnemonic = state.mnemonic
75− const passphrase = state.passphrase
72+ const mnemonic = state.mnemonic;
73+ const passphrase = state.passphrase;
7674 this.attemptWalletDerivation(
7775 mnemonic,
7876 hdpath === '' ? DEFAULT_HD_PATH : hdpath,
7977 passphrase
80− )
78+ );
8179 return {
82− hdpath
83− }
84− })
80+ hdpath,
81+ };
82+ });
8583 }
8684
8785 attemptWalletDerivation(mnemonic, hdpath, passphrase) {
88− const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props
89− const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase)
90− setWallet(wallet)
91− setAuthMnemonic(Just(mnemonic))
92− setWalletHdPath(hdpath)
86+ const { setWallet, setAuthMnemonic, setWalletHdPath } = this.props;
87+ const wallet = walletFromMnemonic(mnemonic, hdpath, passphrase);
88+ setWallet(wallet);
89+ setAuthMnemonic(Just(mnemonic));
90+ setWalletHdPath(hdpath);
9391 }
9492
9593 render() {
96− const { pushRoute, popRoute, wallet } = this.props
97− const {
98− mnemonic,
99− hdpath,
100− exampleMnemonic,
101− passphrase
102− } = this.state
94+ const { pushRoute, popRoute, wallet } = this.props;
95+ const { mnemonic, hdpath, exampleMnemonic, passphrase } = this.state;
10396
10497 return (
105− <Row>
106− <Col>
107− <H1 className={'mb-4'}>{ 'Enter Your Mnemonic' }</H1>
108− <InputCaption>
109− { "Please enter your BIP39 mnemonic here." }
110− </InputCaption>
111−
112− <MnemonicInput
113− className='pt-8'
114− prop-size='md'
115− prop-format='innerLabel'
116− type='text'
117− name='mnemonic'
118− placeholder={ `e.g. ${exampleMnemonic}` }
119− onChange={ this.handleMnemonicInput }
120− value={ mnemonic }
121− autocomplete='off'
122− autoFocus>
123− <InnerLabel>{'Mnemonic'}</InnerLabel>
124− </MnemonicInput>
125−
126− <InputCaption>
98+ <Row>
99+ <Col>
100+ <H1 className={'mb-4'}>{'Enter Your Mnemonic'}</H1>
101+ <InputCaption>
102+ {'Please enter your BIP39 mnemonic here.'}
103+ </InputCaption>
104+
105+ <MnemonicInput
106+ className="pt-8"
107+ prop-size="md"
108+ prop-format="innerLabel"
109+ type="text"
110+ name="mnemonic"
111+ placeholder={`e.g. ${exampleMnemonic}`}
112+ onChange={this.handleMnemonicInput}
113+ value={mnemonic}
114+ autocomplete="off"
115+ autoFocus>
116+ <InnerLabel>{'Mnemonic'}</InnerLabel>
117+ </MnemonicInput>
118+
119+ <InputCaption>
127120 {`If your wallet requires a passphrase, you may enter it below.`}
128− </InputCaption>
129−
130− <Input
131− className='pt-8'
132− prop-size='md'
133− prop-format='innerLabel'
134− name='passphrase'
135− type='password'
136− value={ passphrase }
137− autocomplete='off'
138− onChange={ this.handlePassphraseInput }>
139− <InnerLabel>{'Passphrase'}</InnerLabel>
140− </Input>
141−
142− <InputCaption>
121+ </InputCaption>
122+
123+ <Input
124+ className="pt-8"
125+ prop-size="md"
126+ prop-format="innerLabel"
127+ name="passphrase"
128+ type="password"
129+ value={passphrase}
130+ autocomplete="off"
131+ onChange={this.handlePassphraseInput}>
132+ <InnerLabel>{'Passphrase'}</InnerLabel>
133+ </Input>
134+
135+ <InputCaption>
143136 {`If you'd like to use a custom derivation path, you may enter it below.`}
144− </InputCaption>
145−
146− <Input
147− className='pt-8 text-mono'
148− prop-size='md'
149− prop-format='innerLabel'
150− name='hdpath'
151− value={ hdpath }
152− autocomplete='off'
153− onChange={ this.handleHdPathInput }>
154− <InnerLabel>{'HD Path'}</InnerLabel>
155− </Input>
156−
157− <Button
158− className={'mt-10'}
159− disabled={ Nothing.hasInstance(wallet) }
160− onClick={ () => {
161− popRoute()
162− pushRoute(ROUTE_NAMES.SHIPS)
163− }
164− }
165− >
166− { 'Continue →' }
167− </Button>
168− </Col>
169− </Row>
170−
171−
172− )
137+ </InputCaption>
138+
139+ <Input
140+ className="pt-8 text-mono"
141+ prop-size="md"
142+ prop-format="innerLabel"
143+ name="hdpath"
144+ value={hdpath}
145+ autocomplete="off"
146+ onChange={this.handleHdPathInput}>
147+ <InnerLabel>{'HD Path'}</InnerLabel>
148+ </Input>
149+
150+ <Button
151+ className={'mt-10'}
152+ disabled={Nothing.hasInstance(wallet)}
153+ onClick={() => {
154+ popRoute();
155+ pushRoute(ROUTE_NAMES.SHIPS);
156+ }}>
157+ {'Continue →'}
158+ </Button>
159+ </Col>
160+ </Row>
161+ );
173162 }
174163}
175164
176−export default Mnemonic
165+export default Mnemonic;
src/bridge/views/Network.js+90−90
@@ -1,65 +1,62 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import { Button } from '../components/Base'
4−import { InnerLabelDropdown } from '../components/Base'
5−import { Row, Col, H1, P } from '../components/Base'
6−import * as azimuth from 'azimuth-js'
7−import Web3 from 'web3'
8−
9−import { CONTRACT_ADDRESSES } from '../lib/contracts'
10−import { NETWORK_NAMES, renderNetworkType } from '../lib/network'
11−import { ROUTE_NAMES } from '../lib/router'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import { Button } from '../components/Base';
4+import { InnerLabelDropdown } from '../components/Base';
5+import { Row, Col, H1, P } from '../components/Base';
6+import * as azimuth from 'azimuth-js';
7+import Web3 from 'web3';
8+
9+import { CONTRACT_ADDRESSES } from '../lib/contracts';
10+import { NETWORK_NAMES, renderNetworkType } from '../lib/network';
11+import { ROUTE_NAMES } from '../lib/router';
1212
1313class Network extends React.Component {
14−
1514 constructor(props) {
16− super(props)
15+ super(props);
1716
1817 this.handleNetworkChange = this.handleNetworkChange.bind(this);
1918 }
2019
2120 handleNetworkChange(network) {
22− const { setNetworkType, setNetwork } = this.props
21+ const { setNetworkType, setNetwork } = this.props;
2322
2423 if (network === NETWORK_NAMES.LOCAL) {
25− setNetworkType(network)
24+ setNetworkType(network);
2625
27− const endpoint = 'ws://localhost:8545'
28− const provider = new Web3.providers.WebsocketProvider(endpoint)
29− const web3 = new Web3(provider)
30− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV)
26+ const endpoint = 'ws://localhost:8545';
27+ const provider = new Web3.providers.WebsocketProvider(endpoint);
28+ const web3 = new Web3(provider);
29+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.DEV);
3130
32− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
31+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
3332 }
3433
3534 if (network === NETWORK_NAMES.ROPSTEN) {
36− setNetworkType(network)
35+ setNetworkType(network);
3736
38− const endpoint =
39− `https://ropsten.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
37+ const endpoint = `https://ropsten.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
4038
41− const provider = new Web3.providers.HttpProvider(endpoint)
42− const web3 = new Web3(provider)
43− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.ROPSTEN)
39+ const provider = new Web3.providers.HttpProvider(endpoint);
40+ const web3 = new Web3(provider);
41+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.ROPSTEN);
4442
45− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
43+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
4644 }
4745
4846 if (network === NETWORK_NAMES.MAINNET) {
49− setNetworkType(network)
47+ setNetworkType(network);
5048
51− const endpoint =
52− `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`
49+ const endpoint = `https://mainnet.infura.io/v3/${process.env.REACT_APP_INFURA_ENDPOINT}`;
5350
54− const provider = new Web3.providers.HttpProvider(endpoint)
55− const web3 = new Web3(provider)
56− const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET)
51+ const provider = new Web3.providers.HttpProvider(endpoint);
52+ const web3 = new Web3(provider);
53+ const contracts = azimuth.initContracts(web3, CONTRACT_ADDRESSES.MAINNET);
5754
58− setNetwork(Maybe.Just(web3), Maybe.Just(contracts))
55+ setNetwork(Maybe.Just(web3), Maybe.Just(contracts));
5956 }
6057
6158 if (network === NETWORK_NAMES.OFFLINE) {
62− setNetworkType(network)
59+ setNetworkType(network);
6360
6461 // NB (jtobin):
6562 //
@@ -72,74 +69,77 @@ class Network extends React.Component {
7269
7370 // Note: localhost:3456 doesn't actually point to anything, we just need
7471 // a provider to initialize the Web3 object
75− const provider = new Web3.providers.HttpProvider("http://localhost:3456")
76− const web3 = new Web3(provider)
72+ const provider = new Web3.providers.HttpProvider('http://localhost:3456');
73+ const web3 = new Web3(provider);
7774
7875 const target =
79− process.env.NODE_ENV === 'development'
80− ? CONTRACT_ADDRESSES.DEV
81− : CONTRACT_ADDRESSES.MAINNET
76+ process.env.NODE_ENV === 'development'
77+ ? CONTRACT_ADDRESSES.DEV
78+ : CONTRACT_ADDRESSES.MAINNET;
8279
83− const contracts = azimuth.initContracts(web3, target)
80+ const contracts = azimuth.initContracts(web3, target);
8481
85− setNetwork(Maybe.Nothing(), Maybe.Just(contracts))
82+ setNetwork(Maybe.Nothing(), Maybe.Just(contracts));
8683 }
8784 }
8885
8986 getNetworkOptions() {
90− return [{
91− title: 'Main Network (default)',
92− value: NETWORK_NAMES.MAINNET
93− }, {
94− title: 'Local Node',
95− value: NETWORK_NAMES.LOCAL
96− }, {
97− title: 'Ropsten',
98− value: NETWORK_NAMES.ROPSTEN
99− }, {
100− type: 'divider'
101− }, {
102− title: 'Offline',
103− value: NETWORK_NAMES.OFFLINE
104− }]
87+ return [
88+ {
89+ title: 'Main Network (default)',
90+ value: NETWORK_NAMES.MAINNET,
91+ },
92+ {
93+ title: 'Local Node',
94+ value: NETWORK_NAMES.LOCAL,
95+ },
96+ {
97+ title: 'Ropsten',
98+ value: NETWORK_NAMES.ROPSTEN,
99+ },
100+ {
101+ type: 'divider',
102+ },
103+ {
104+ title: 'Offline',
105+ value: NETWORK_NAMES.OFFLINE,
106+ },
107+ ];
105108 }
106109
107110 render() {
108− const { networkType, pushRoute } = this.props
109− const networkOptions = this.getNetworkOptions()
111+ const { networkType, pushRoute } = this.props;
112+ const networkOptions = this.getNetworkOptions();
110113
111114 return (
112− <Row>
113− <Col>
114− <H1>{ 'Select Network' }</H1>
115−
116− <P>
117− {
118− "Please select the Ethereum node you'd like to send " +
119− "transactions to. For highly valuable keys, please select " +
120− "offline mode."
121− }
122− </P>
123−
124− <InnerLabelDropdown
125− className={'mt-6'}
126− title={'Node:'}
127− handleUpdate={this.handleNetworkChange}
128− options={networkOptions}
129− currentSelectionTitle={renderNetworkType(networkType)}>
130− </InnerLabelDropdown>
131−
132− <Button
133− className={'mt-10'}
134− onClick={ () => pushRoute(ROUTE_NAMES.WALLET) }
135− >
136− { 'Continue →' }
137− </Button>
138−
139− </Col>
140− </Row>
141− )
115+ <Row>
116+ <Col>
117+ <H1>{'Select Network'}</H1>
118+
119+ <P>
120+ {"Please select the Ethereum node you'd like to send " +
121+ 'transactions to. For highly valuable keys, please select ' +
122+ 'offline mode.'}
123+ </P>
124+
125+ <InnerLabelDropdown
126+ className={'mt-6'}
127+ title={'Node:'}
128+ handleUpdate={this.handleNetworkChange}
129+ options={networkOptions}
130+ currentSelectionTitle={renderNetworkType(
131+ networkType
132+ )}></InnerLabelDropdown>
133+
134+ <Button
135+ className={'mt-10'}
136+ onClick={() => pushRoute(ROUTE_NAMES.WALLET)}>
137+ {'Continue →'}
138+ </Button>
139+ </Col>
140+ </Row>
141+ );
142142 }
143143}
144144
145−export default Network
145+export default Network;
src/bridge/views/Point.js+85−87
@@ -1,30 +1,28 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import { pour } from 'sigil-js'
4−import * as ob from 'urbit-ob'
5−import * as azimuth from 'azimuth-js'
6−import * as need from '../lib/need'
7−
8−import PointList from '../components/PointList'
9−import ReactSVGComponents from '../components/ReactSVGComponents'
10−import KeysAndMetadata from './Point/KeysAndMetadata'
11−import Actions from './Point/Actions'
12−import { Row, Col, H1, H3 } from '../components/Base'
13−
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import { pour } from 'sigil-js';
4+import * as ob from 'urbit-ob';
5+import * as azimuth from 'azimuth-js';
6+import * as need from '../lib/need';
7+
8+import PointList from '../components/PointList';
9+import ReactSVGComponents from '../components/ReactSVGComponents';
10+import KeysAndMetadata from './Point/KeysAndMetadata';
11+import Actions from './Point/Actions';
12+import { Row, Col, H1, H3 } from '../components/Base';
1413
1514class Point extends React.Component {
16−
1715 constructor(props) {
18− super(props)
16+ super(props);
1917
2018 this.state = {
2119 spawned: [],
22− invites: Nothing()
23− }
20+ invites: Nothing(),
21+ };
2422 }
2523
2624 componentDidMount() {
27− this.cachePoints()
25+ this.cachePoints();
2826 }
2927
3028 componentDidUpdate(prevProps) {
@@ -35,118 +33,118 @@ class Point extends React.Component {
3533 // actually leave the Point component, and componentDidMount will not fire
3634 // again. But, we can force a call to cachePoints here.
3735 //
38− const oldCursor = prevProps.pointCursor.getOrElse(true)
39− const newCursor = this.props.pointCursor.getOrElse(true)
36+ const oldCursor = prevProps.pointCursor.getOrElse(true);
37+ const newCursor = this.props.pointCursor.getOrElse(true);
4038
4139 if (oldCursor !== newCursor) {
42− this.cachePoints()
40+ this.cachePoints();
4341 }
4442 }
4543
4644 cachePoints() {
47− const { web3, contracts, pointCursor, addToPointCache } = this.props
45+ const { web3, contracts, pointCursor, addToPointCache } = this.props;
4846
4947 web3.chain(_ =>
50− contracts.chain(ctrcs =>
51− pointCursor.chain(point => {
52− azimuth.azimuth.getPoint(ctrcs, point)
53− .then(details => addToPointCache({ [point]: details }));
54− azimuth.delegatedSending.getTotalUsableInvites(ctrcs, point)
55− .then(count => this.setState({ invites: Just(count) }));
56− this.updateSpawned(ctrcs, point);
57− })));
48+ contracts.chain(ctrcs =>
49+ pointCursor.chain(point => {
50+ azimuth.azimuth
51+ .getPoint(ctrcs, point)
52+ .then(details => addToPointCache({ [point]: details }));
53+ azimuth.delegatedSending
54+ .getTotalUsableInvites(ctrcs, point)
55+ .then(count => this.setState({ invites: Just(count) }));
56+ this.updateSpawned(ctrcs, point);
57+ })
58+ )
59+ );
5860 }
5961
6062 updateSpawned = contracts => {
61− const { pointCursor } = this.props
63+ const { pointCursor } = this.props;
6264
6365 pointCursor.matchWith({
6466 Nothing: _ => {},
6567 Just: point => {
66− azimuth.azimuth.getSpawned(contracts, point.value)
67− .then(spawned => this.setState({ spawned }))
68− }
69− })
70−
71− }
68+ azimuth.azimuth
69+ .getSpawned(contracts, point.value)
70+ .then(spawned => this.setState({ spawned }));
71+ },
72+ });
73+ };
7274
7375 render() {
74−
7576 const {
76− web3, popRoute, pushRoute, wallet,
77− setPointCursor, pointCache
77+ web3,
78+ popRoute,
79+ pushRoute,
80+ wallet,
81+ setPointCursor,
82+ pointCache,
7883 } = this.props;
7984
80− const { spawned } = this.state
85+ const { spawned } = this.state;
8186
8287 const point = need.pointCursor(this.props);
8388
8489 const pointDetails =
85− point in pointCache
86− ? Just(pointCache[point])
87− : Nothing()
90+ point in pointCache ? Just(pointCache[point]) : Nothing();
8891
89− const name = ob.patp(point)
92+ const name = ob.patp(point);
9093
9194 const sigil = pour({
9295 patp: name,
9396 renderer: ReactSVGComponents,
94− size: 256
95− })
97+ size: 256,
98+ });
9699
97− const online = Just.hasInstance(web3)
100+ const online = Just.hasInstance(web3);
98101
99− const authenticated = Just.hasInstance(wallet)
102+ const authenticated = Just.hasInstance(wallet);
100103
101104 const routeHandler = route => {
102− popRoute()
103− pushRoute(route)
104− }
105+ popRoute();
106+ pushRoute(route);
107+ };
105108
106109 const issuedPointList =
107− spawned.length === 0
108− ? <div />
109− : <div>
110− <H3>{ 'Issued Points' }</H3>
110+ spawned.length === 0 ? (
111+ <div />
112+ ) : (
113+ <div>
114+ <H3>{'Issued Points'}</H3>
111115
112116 <PointList
113− setPointCursor={ setPointCursor }
114− routeHandler={ routeHandler }
115− points={ spawned }
117+ setPointCursor={setPointCursor}
118+ routeHandler={routeHandler}
119+ points={spawned}
116120 />
117121 </div>
118−
119−
122+ );
120123
121124 return (
122− <Row>
123− <Col>
124− <div className={'mt-12 pt-6'}>
125− { sigil }
126− </div>
127− <H1><code>{ name }</code></H1>
128− {
129− authenticated
130− ? <Actions
131− pushRoute={ pushRoute }
132− online={ online }
133− wallet={ wallet }
134− point={ point }
135− pointDetails={ pointDetails }
136− invites={ this.state.invites } />
137− : null
138− }
139−
140− {
141− online
142− ? <KeysAndMetadata pointDetails={ pointDetails } />
143− : <div />
144− }
145− { issuedPointList }
125+ <Row>
126+ <Col>
127+ <div className={'mt-12 pt-6'}>{sigil}</div>
128+ <H1>
129+ <code>{name}</code>
130+ </H1>
131+ {authenticated ? (
132+ <Actions
133+ pushRoute={pushRoute}
134+ online={online}
135+ wallet={wallet}
136+ point={point}
137+ pointDetails={pointDetails}
138+ invites={this.state.invites}
139+ />
140+ ) : null}
141+
142+ {online ? <KeysAndMetadata pointDetails={pointDetails} /> : <div />}
143+ {issuedPointList}
146144 </Col>
147145 </Row>
148− )
146+ );
149147 }
150148}
151149
152−export default Point
150+export default Point;
src/bridge/views/Point/Actions.js+132−163
@@ -1,265 +1,234 @@
1−import React from 'react'
2−import * as need from '../../lib/need'
3−import {
4− ETH_ZERO_ADDR,
5− CURVE_ZERO_ADDR,
6− eqAddr
7−} from '../../lib/wallet'
8−import { Row, Col, H2, P } from '../../components/Base'
9−import { Button } from '../../components/Base'
10−import { ROUTE_NAMES } from '../../lib/router'
11−import { azimuth } from 'azimuth-js'
1+import React from 'react';
2+import * as need from '../../lib/need';
3+import { ETH_ZERO_ADDR, CURVE_ZERO_ADDR, eqAddr } from '../../lib/wallet';
4+import { Row, Col, H2, P } from '../../components/Base';
5+import { Button } from '../../components/Base';
6+import { ROUTE_NAMES } from '../../lib/router';
7+import { azimuth } from 'azimuth-js';
128
139const isPlanet = point =>
14− (azimuth.getPointSize(point) === azimuth.PointSize.Planet);
15−const isStar = point =>
16− (azimuth.getPointSize(point) === azimuth.PointSize.Star);
10+ azimuth.getPointSize(point) === azimuth.PointSize.Planet;
11+const isStar = point => azimuth.getPointSize(point) === azimuth.PointSize.Star;
1712
18−const Actions = (props) => {
19− const {
20− pushRoute, online,
21− point, pointDetails, invites
22− } = props
13+const Actions = props => {
14+ const { pushRoute, online, point, pointDetails, invites } = props;
2315
2416 const addr = need.address(props);
2517
2618 const isOwner = pointDetails.matchWith({
2719 Nothing: _ => false,
28− Just: details => eqAddr(details.value.owner, addr)
20+ Just: details => eqAddr(details.value.owner, addr),
2921 });
3022 const isActive = pointDetails.matchWith({
3123 Nothing: _ => false,
32− Just: details => details.value.active
24+ Just: details => details.value.active,
3325 });
34− const isActiveOwner = (isOwner && isActive);
26+ const isActiveOwner = isOwner && isActive;
3527 const canSetSpawnProxy = isActiveOwner;
3628 const canSetManagementProxy = isActiveOwner;
3729
38− const canManage = (isOwner || pointDetails.matchWith({
39− Nothing: _ => false,
40− Just: details => eqAddr(details.value.managementProxy, addr)
41− }));
42− const canConfigureKeys = (canManage && isActive);
30+ const canManage =
31+ isOwner ||
32+ pointDetails.matchWith({
33+ Nothing: _ => false,
34+ Just: details => eqAddr(details.value.managementProxy, addr),
35+ });
36+ const canConfigureKeys = canManage && isActive;
4337
44− const canIssueChild =
45− pointDetails.matchWith({
46− Nothing: () => false,
47− Just: (details) => {
48− const hasPermission =
49− isOwner
50− || eqAddr(details.value.spawnProxy, addr)
38+ const canIssueChild = pointDetails.matchWith({
39+ Nothing: () => false,
40+ Just: details => {
41+ const hasPermission = isOwner || eqAddr(details.value.spawnProxy, addr);
5142
52− const isBooted =
53− details.value.keyRevisionNumber > 0
43+ const isBooted = details.value.keyRevisionNumber > 0;
5444
55− const isNotPlanet = !isPlanet(point)
45+ const isNotPlanet = !isPlanet(point);
5646
57− return hasPermission && isBooted && isNotPlanet
58− }
59− })
47+ return hasPermission && isBooted && isNotPlanet;
48+ },
49+ });
6050
61− const hasInvites =
62− invites.matchWith({
63− Nothing: () => false,
64− Just: (count) => (count.value > 0)
65− });
51+ const hasInvites = invites.matchWith({
52+ Nothing: () => false,
53+ Just: count => count.value > 0,
54+ });
6655
67− const planet = isPlanet(point)
68− const star = isStar(point)
56+ const planet = isPlanet(point);
57+ const star = isStar(point);
6958
70− const canTransfer =
71− pointDetails.matchWith({
72− Nothing: () => false,
73− Just: (deets) =>
74− eqAddr(deets.value.transferProxy, addr) ||
75− eqAddr(deets.value.owner, addr)
76− })
59+ const canTransfer = pointDetails.matchWith({
60+ Nothing: () => false,
61+ Just: deets =>
62+ eqAddr(deets.value.transferProxy, addr) ||
63+ eqAddr(deets.value.owner, addr),
64+ });
7765
78− const canGenKeyfile =
79− pointDetails.matchWith({
80− Nothing: () => false,
81− Just: (deets) => {
82− const hasPermission =
83− eqAddr(deets.value.owner, addr)
84− || eqAddr(deets.value.managementProxy, addr)
66+ const canGenKeyfile = pointDetails.matchWith({
67+ Nothing: () => false,
68+ Just: deets => {
69+ const hasPermission =
70+ eqAddr(deets.value.owner, addr) ||
71+ eqAddr(deets.value.managementProxy, addr);
8572
86− const isBooted =
87− deets.value.keyRevisionNumber > 0
73+ const isBooted = deets.value.keyRevisionNumber > 0;
8874
89− return hasPermission && isBooted
90− }
91− })
75+ return hasPermission && isBooted;
76+ },
77+ });
9278
93− const canAcceptTransfer =
94− pointDetails.matchWith({
95− Nothing: () => false,
96− Just: (deets) =>
97− eqAddr(deets.value.transferProxy, addr)
98− })
79+ const canAcceptTransfer = pointDetails.matchWith({
80+ Nothing: () => false,
81+ Just: deets => eqAddr(deets.value.transferProxy, addr),
82+ });
9983
100− const canCancelTransfer =
101− pointDetails.matchWith({
102− Nothing: () => false,
103− Just: (deets) =>
104− eqAddr(deets.value.owner, addr) &&
105− !eqAddr(deets.value.transferProxy, ETH_ZERO_ADDR)
106− })
84+ const canCancelTransfer = pointDetails.matchWith({
85+ Nothing: () => false,
86+ Just: deets =>
87+ eqAddr(deets.value.owner, addr) &&
88+ !eqAddr(deets.value.transferProxy, ETH_ZERO_ADDR),
89+ });
10790
10891 const displayReminder = pointDetails.matchWith({
10992 Nothing: () => false,
110− Just: (deets) => {
111− return deets.value.encryptionKey === CURVE_ZERO_ADDR
112− && deets.value.authenticationKey === CURVE_ZERO_ADDR
113− }
114− })
93+ Just: deets => {
94+ return (
95+ deets.value.encryptionKey === CURVE_ZERO_ADDR &&
96+ deets.value.authenticationKey === CURVE_ZERO_ADDR
97+ );
98+ },
99+ });
115100
116101 let inviteAction = null;
117102 if (planet) {
118103 inviteAction = (
119104 <Button
120− disabled={(!isActiveOwner || !online || !hasInvites)}
105+ disabled={!isActiveOwner || !online || !hasInvites}
121106 prop-size={'sm'}
122107 prop-type={'link'}
123− onClick={ () => {
108+ onClick={() => {
124109 pushRoute(ROUTE_NAMES.INVITES_SEND);
125− }}
126− >
127− { 'Send invites (' }
128− { invites.matchWith({
129− Nothing: () => '?',
130− Just: (count) => count.value
131− })
132− }
133− { ')' }
110+ }}>
111+ {'Send invites ('}
112+ {invites.matchWith({
113+ Nothing: () => '?',
114+ Just: count => count.value,
115+ })}
116+ {')'}
134117 </Button>
135118 );
136119 }
137120 if (star) {
138121 inviteAction = (
139122 <Button
140− disabled={(!(isActiveOwner && canIssueChild) || !online)}
123+ disabled={!(isActiveOwner && canIssueChild) || !online}
141124 prop-size={'sm'}
142125 prop-type={'link'}
143− onClick={ () => {
126+ onClick={() => {
144127 pushRoute(ROUTE_NAMES.INVITES_MANAGE);
145− }}
146− >
147− { 'Manage invites' }
128+ }}>
129+ {'Manage invites'}
148130 </Button>
149131 );
150132 }
151133
152134 return (
153135 <div>
154− <H2>{ 'Actions' }</H2>
155− {
156− displayReminder
157− ? <P>{`Before you can issue child points or generate your Arvo
136+ <H2>{'Actions'}</H2>
137+ {displayReminder ? (
138+ <P>{`Before you can issue child points or generate your Arvo
158139 keyfile, you need to set your public keys.`}</P>
159− : ''
160− }
140+ ) : (
141+ ''
142+ )}
161143 <Row>
162144 <Col className={'flex flex-column items-start col-md-4'}>
163145 <Button
164146 prop-size={'sm'}
165147 prop-type={'link'}
166− disabled={ (online || planet) && !canIssueChild }
167− onClick={ () => {
168− pushRoute(ROUTE_NAMES.ISSUE_CHILD)
169− }}
170− >
171− { 'Issue child' }
148+ disabled={(online || planet) && !canIssueChild}
149+ onClick={() => {
150+ pushRoute(ROUTE_NAMES.ISSUE_CHILD);
151+ }}>
152+ {'Issue child'}
172153 </Button>
173154
174155 <Button
175156 prop-size={'sm'}
176157 prop-type={'link'}
177− disabled={ online && !canAcceptTransfer }
178− onClick={ () => {
179− pushRoute(ROUTE_NAMES.ACCEPT_TRANSFER)
180− }}
181− >
182− { 'Accept incoming transfer' }
158+ disabled={online && !canAcceptTransfer}
159+ onClick={() => {
160+ pushRoute(ROUTE_NAMES.ACCEPT_TRANSFER);
161+ }}>
162+ {'Accept incoming transfer'}
183163 </Button>
184164
185165 <Button
186166 prop-size={'sm'}
187167 prop-type={'link'}
188− disabled={ online && !canCancelTransfer }
189− onClick={ () => {
190− pushRoute(ROUTE_NAMES.CANCEL_TRANSFER)
191− }}
192− >
193− { 'Cancel outgoing transfer' }
168+ disabled={online && !canCancelTransfer}
169+ onClick={() => {
170+ pushRoute(ROUTE_NAMES.CANCEL_TRANSFER);
171+ }}>
172+ {'Cancel outgoing transfer'}
194173 </Button>
195174
196175 <Button
197176 prop-size={'sm'}
198177 prop-type={'link'}
199− disabled={ online && !canGenKeyfile }
200− onClick={ () => {
201− pushRoute(ROUTE_NAMES.GEN_KEYFILE)
202− }}
203− >
204− { 'Generate Arvo keyfile' }
178+ disabled={online && !canGenKeyfile}
179+ onClick={() => {
180+ pushRoute(ROUTE_NAMES.GEN_KEYFILE);
181+ }}>
182+ {'Generate Arvo keyfile'}
205183 </Button>
206−
207184 </Col>
208185 <Col className={'flex flex-column items-start col-md-4'}>
209−
210186 <Button
211− disabled={ online && !canSetSpawnProxy }
187+ disabled={online && !canSetSpawnProxy}
212188 prop-size={'sm'}
213189 prop-type={'link'}
214− onClick={ () => {
215− pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY)
216− }}
217− >
218− { 'Change spawn proxy' }
190+ onClick={() => {
191+ pushRoute(ROUTE_NAMES.SET_SPAWN_PROXY);
192+ }}>
193+ {'Change spawn proxy'}
219194 </Button>
220195
221196 <Button
222− disabled={ online && !canSetManagementProxy }
197+ disabled={online && !canSetManagementProxy}
223198 prop-size={'sm'}
224199 prop-type={'link'}
225− onClick={ () => {
226− pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY)
227− }}
228− >
229− { 'Change management proxy' }
200+ onClick={() => {
201+ pushRoute(ROUTE_NAMES.SET_MANAGEMENT_PROXY);
202+ }}>
203+ {'Change management proxy'}
230204 </Button>
231205
232206 <Button
233− disabled={ online && !canConfigureKeys }
207+ disabled={online && !canConfigureKeys}
234208 prop-size={'sm'}
235209 prop-type={'link'}
236− onClick={ () => {
237− pushRoute(ROUTE_NAMES.SET_KEYS)
238− }}
239− >
240− { 'Set network keys' }
210+ onClick={() => {
211+ pushRoute(ROUTE_NAMES.SET_KEYS);
212+ }}>
213+ {'Set network keys'}
241214 </Button>
242215
243216 <Button
244− disabled={ online && !canTransfer }
217+ disabled={online && !canTransfer}
245218 prop-size={'sm'}
246219 prop-type={'link'}
247− onClick={ () => {
248− pushRoute(ROUTE_NAMES.TRANSFER)
249− }}
250− >
251− { 'Transfer' }
220+ onClick={() => {
221+ pushRoute(ROUTE_NAMES.TRANSFER);
222+ }}>
223+ {'Transfer'}
252224 </Button>
253−
254225 </Col>
255226 <Col className={'flex flex-column items-start col-md-4'}>
256−
257− { inviteAction }
258−
227+ {inviteAction}
259228 </Col>
260229 </Row>
261230 </div>
262− )
263−}
231+ );
232+};
264233
265−export default Actions
234+export default Actions;
src/bridge/views/Point/KeysAndMetadata.js+124−146
@@ -1,181 +1,159 @@
1−import React from 'react'
2−import { Row, Col, H2, H4 } from '../../components/Base'
1+import React from 'react';
2+import { Row, Col, H2, H4 } from '../../components/Base';
33
4−import { ETH_ZERO_ADDR, eqAddr } from '../../lib/wallet'
4+import { ETH_ZERO_ADDR, eqAddr } from '../../lib/wallet';
55
66const NULL_KEY =
7− '0x0000000000000000000000000000000000000000000000000000000000000000'
7+ '0x0000000000000000000000000000000000000000000000000000000000000000';
88
9−const renderAddress = (addr) =>
10− eqAddr(addr, ETH_ZERO_ADDR)
11− ? <div>
12− { '(not set)' }
13− </div>
14− : <div>
15− <div><code>{ addr.slice(0, 14) }</code></div>
16− <div><code>{ addr.slice(14, 28) }</code></div>
17− <div><code>{ addr.slice(28) }</code></div>
9+const renderAddress = addr =>
10+ eqAddr(addr, ETH_ZERO_ADDR) ? (
11+ <div>{'(not set)'}</div>
12+ ) : (
13+ <div>
14+ <div>
15+ <code>{addr.slice(0, 14)}</code>
16+ </div>
17+ <div>
18+ <code>{addr.slice(14, 28)}</code>
19+ </div>
20+ <div>
21+ <code>{addr.slice(28)}</code>
22+ </div>
1823 </div>
19−
20−const renderNetworkKey = (hex) => {
21− const sl = i => hex.slice(i, i + 4)
22− const rowFrom = i => `${sl(i)}.${sl(i + 4)}.${sl(i + 8)}.${sl(i + 12)}`
23− return (
24− hex === NULL_KEY
25− ? <div>
26− { '(not set)' }
24+ );
25+
26+const renderNetworkKey = hex => {
27+ const sl = i => hex.slice(i, i + 4);
28+ const rowFrom = i => `${sl(i)}.${sl(i + 4)}.${sl(i + 8)}.${sl(i + 12)}`;
29+ return hex === NULL_KEY ? (
30+ <div>{'(not set)'}</div>
31+ ) : (
32+ <div>
33+ <div>
34+ <code>{hex.slice(0, 2)}</code>
35+ </div>
36+ <div>
37+ <code>{rowFrom(2)}</code>
38+ </div>
39+ <div>
40+ <code>{rowFrom(18)}</code>
41+ </div>
42+ <div>
43+ <code>{rowFrom(34)}</code>
2744 </div>
28− : <div>
29− <div><code>{ hex.slice(0, 2) }</code></div>
30− <div>
31− <code>{ rowFrom(2) }</code>
32− </div>
33− <div>
34− <code>{ rowFrom(18) }</code>
35− </div>
36− <div>
37− <code>{ rowFrom(34) }</code>
38− </div>
39− <div>
40− <code>{ rowFrom(50) }</code>
41− </div>
45+ <div>
46+ <code>{rowFrom(50)}</code>
4247 </div>
43− )
44−}
48+ </div>
49+ );
50+};
4551
46−const KeysAndMetadata = (props) => {
47− const { pointDetails } = props
52+const KeysAndMetadata = props => {
53+ const { pointDetails } = props;
4854
4955 return (
5056 <Row>
5157 <Col>
52− <H2>{ 'Ownership and Proxy Addresses' }</H2>
58+ <H2>{'Ownership and Proxy Addresses'}</H2>
5359
54− <Row>
55− <Col className={'flex flex-column items-start col-md-3'}>
56− <H4>{ 'Owner' }</H4>
57− {
58− pointDetails.matchWith({
60+ <Row>
61+ <Col className={'flex flex-column items-start col-md-3'}>
62+ <H4>{'Owner'}</H4>
63+ {pointDetails.matchWith({
5964 Nothing: () => <div />,
60− Just: (deets) => renderAddress(deets.value.owner)
61− })
62− }
63− </Col>
64−
65+ Just: deets => renderAddress(deets.value.owner),
66+ })}
67+ </Col>
6568
66− <Col className={'flex flex-column items-start col-md-3'}>
67− <H4>{ 'Transfer Proxy' }</H4>
68− {
69− pointDetails.matchWith({
69+ <Col className={'flex flex-column items-start col-md-3'}>
70+ <H4>{'Transfer Proxy'}</H4>
71+ {pointDetails.matchWith({
7072 Nothing: () => <div />,
71− Just: (deets) => renderAddress(deets.value.transferProxy)
72− })
73− }
74− </Col>
75−
73+ Just: deets => renderAddress(deets.value.transferProxy),
74+ })}
75+ </Col>
7676
77− <Col className={'flex flex-column items-start col-md-3'}>
78− <H4>{ 'Spawn Proxy' }</H4>
79− {
80− pointDetails.matchWith({
77+ <Col className={'flex flex-column items-start col-md-3'}>
78+ <H4>{'Spawn Proxy'}</H4>
79+ {pointDetails.matchWith({
8180 Nothing: () => <div />,
82− Just: (deets) => renderAddress(deets.value.spawnProxy)
83− })
84− }
85− </Col>
81+ Just: deets => renderAddress(deets.value.spawnProxy),
82+ })}
83+ </Col>
8684
87−
88− <Col className={'flex flex-column items-start col-md-3'}>
89− <H4>{ 'Mgmt Proxy' }</H4>
90− {
91− pointDetails.matchWith({
85+ <Col className={'flex flex-column items-start col-md-3'}>
86+ <H4>{'Mgmt Proxy'}</H4>
87+ {pointDetails.matchWith({
9288 Nothing: () => <div />,
93− Just: (deets) => renderAddress(deets.value.managementProxy)
94− })
95− }
96− </Col>
97− </Row>
98−
99−
100− <UrbitNetworking pointDetails={ pointDetails } />
101−
89+ Just: deets => renderAddress(deets.value.managementProxy),
90+ })}
91+ </Col>
92+ </Row>
10293
94+ <UrbitNetworking pointDetails={pointDetails} />
10395 </Col>
10496 </Row>
105− )
106−}
97+ );
98+};
10799
108100const UrbitNetworking = props => {
109− const { pointDetails } = props
101+ const { pointDetails } = props;
110102
111103 const booted = pointDetails.matchWith({
112104 Nothing: _ => false,
113− Just: (details) => details.value.keyRevisionNumber > 0
114− })
105+ Just: details => details.value.keyRevisionNumber > 0,
106+ });
115107
116− const body =
117− !booted
118− ? <div>
119− { '(not set)' }
120− </div>
121− : <Row>
122− <Col className={'flex flex-column items-start col-md-4'}>
123− <H4>{ 'Authentication' }</H4>
124− {
125− pointDetails.matchWith({
126− Nothing: () => <div />,
127− Just: (deets) =>
128− renderNetworkKey(deets.value.authenticationKey)
129− })
130− }
131− </Col>
132−
133− <Col className={'flex flex-column items-start col-md-4'}>
134− <H4>{ 'Encryption' }</H4>
135− {
136− pointDetails.matchWith({
137− Nothing: () => <div />,
138− Just: (deets) =>
139− renderNetworkKey(deets.value.encryptionKey)
140− })
141− }
142− </Col>
143−
144− <Col className={'flex flex-column items-start col-md-4'}>
145− <H4>{ 'Revision' }</H4>
146− {
147− pointDetails.matchWith({
148− Nothing: () => '-',
149− Just: (deets) => deets.value.keyRevisionNumber
150− })
151− }
152−
153−
154− <H4>{ 'Continuity era' }</H4>
155− {
156− pointDetails.matchWith({
157− Nothing: () => '-',
158− Just: (deets) => deets.value.continuityNumber
159− })
160− }
161−
162− <H4>{ 'Crypto suite version' }</H4>
163− {
164− pointDetails.matchWith({
165− Nothing: () => '-',
166− Just: (deets) => deets.value.cryptoSuiteVersion
167− })
168− }
169− </Col>
170− </Row>
108+ const body = !booted ? (
109+ <div>{'(not set)'}</div>
110+ ) : (
111+ <Row>
112+ <Col className={'flex flex-column items-start col-md-4'}>
113+ <H4>{'Authentication'}</H4>
114+ {pointDetails.matchWith({
115+ Nothing: () => <div />,
116+ Just: deets => renderNetworkKey(deets.value.authenticationKey),
117+ })}
118+ </Col>
119+
120+ <Col className={'flex flex-column items-start col-md-4'}>
121+ <H4>{'Encryption'}</H4>
122+ {pointDetails.matchWith({
123+ Nothing: () => <div />,
124+ Just: deets => renderNetworkKey(deets.value.encryptionKey),
125+ })}
126+ </Col>
127+
128+ <Col className={'flex flex-column items-start col-md-4'}>
129+ <H4>{'Revision'}</H4>
130+ {pointDetails.matchWith({
131+ Nothing: () => '-',
132+ Just: deets => deets.value.keyRevisionNumber,
133+ })}
134+
135+ <H4>{'Continuity era'}</H4>
136+ {pointDetails.matchWith({
137+ Nothing: () => '-',
138+ Just: deets => deets.value.continuityNumber,
139+ })}
140+
141+ <H4>{'Crypto suite version'}</H4>
142+ {pointDetails.matchWith({
143+ Nothing: () => '-',
144+ Just: deets => deets.value.cryptoSuiteVersion,
145+ })}
146+ </Col>
147+ </Row>
148+ );
171149
172150 return (
173151 <div>
174− <H2>{ 'Public Keys' }</H2>
152+ <H2>{'Public Keys'}</H2>
175153
176− { body }
154+ {body}
177155 </div>
178− )
179−}
156+ );
157+};
180158
181−export default KeysAndMetadata
159+export default KeysAndMetadata;
src/bridge/views/Points.js+146−138
@@ -1,28 +1,22 @@
1−import React from 'react'
2−import { Row, Col, H1, H2, P } from '../components/Base'
3−import { Button } from '../components/Base'
4−import * as azimuth from 'azimuth-js'
5−
6−import PointList from '../components/PointList'
7−import { NETWORK_NAMES } from '../lib/network'
8−import { ROUTE_NAMES } from '../lib/router'
9−import {
10− ETH_ZERO_ADDR,
11− eqAddr
12− } from '../lib/wallet'
1+import React from 'react';
2+import { Row, Col, H1, H2, P } from '../components/Base';
3+import { Button } from '../components/Base';
4+import * as azimuth from 'azimuth-js';
5+
6+import PointList from '../components/PointList';
7+import { NETWORK_NAMES } from '../lib/network';
8+import { ROUTE_NAMES } from '../lib/router';
9+import { ETH_ZERO_ADDR, eqAddr } from '../lib/wallet';
1310
1411const hasTransferProxy = (cache, point) =>
15− point in cache
16− ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR)
17− : false
12+ point in cache ? !eqAddr(cache[point].transferProxy, ETH_ZERO_ADDR) : false;
1813
1914class Points extends React.Component {
20−
2115 constructor(props) {
22− super(props)
16+ super(props);
2317
24− const { networkType } = props
25− const loading = networkType !== NETWORK_NAMES.OFFLINE
18+ const { networkType } = props;
19+ const loading = networkType !== NETWORK_NAMES.OFFLINE;
2620
2721 this.state = {
2822 points: [],
@@ -31,219 +25,233 @@ class Points extends React.Component {
3125 voting: [],
3226 spawning: [],
3327 eclipticOwner: false,
34− loading: loading
35− }
28+ loading: loading,
29+ };
3630 }
3731
3832 componentDidMount() {
39− const { web3, wallet, contracts, addToPointCache } = this.props
33+ const { web3, wallet, contracts, addToPointCache } = this.props;
4034
4135 web3.chain(_ =>
42− contracts.chain(ctrcs =>
43− wallet.chain(async wal => {
44− const addr = wal.address
36+ contracts.chain(ctrcs =>
37+ wallet.chain(async wal => {
38+ const addr = wal.address;
4539
46− const points =
47− await azimuth.azimuth.getOwnedPoints(ctrcs, addr)
40+ const points = await azimuth.azimuth.getOwnedPoints(ctrcs, addr);
4841
49− const incoming =
50− await azimuth.azimuth.getTransferringFor(ctrcs, addr)
42+ const incoming = await azimuth.azimuth.getTransferringFor(
43+ ctrcs,
44+ addr
45+ );
5146
52− const managing =
53− await azimuth.azimuth.getManagerFor(ctrcs, addr)
47+ const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
5448
55− const voting =
56− await azimuth.azimuth.getVotingFor(ctrcs, addr)
49+ const voting = await azimuth.azimuth.getVotingFor(ctrcs, addr);
5750
58− const spawning =
59− await azimuth.azimuth.getSpawningFor(ctrcs, addr)
51+ const spawning = await azimuth.azimuth.getSpawningFor(ctrcs, addr);
6052
61− const owner =
62− await azimuth.ecliptic.owner(ctrcs)
53+ const owner = await azimuth.ecliptic.owner(ctrcs);
6354
64− const eclipticOwner = eqAddr(addr, owner)
55+ const eclipticOwner = eqAddr(addr, owner);
6556
66− this.setState({
67− points,
68− incoming,
69− managing,
70− voting,
71− spawning,
72− eclipticOwner,
73− loading: false
74− })
57+ this.setState({
58+ points,
59+ incoming,
60+ managing,
61+ voting,
62+ spawning,
63+ eclipticOwner,
64+ loading: false,
65+ });
7566
76− this.cachePoints(ctrcs, addToPointCache, points)
77− })))
67+ this.cachePoints(ctrcs, addToPointCache, points);
68+ })
69+ )
70+ );
7871 }
7972
8073 cachePoints(contracts, addToPointCache, points) {
8174 points.forEach(point => {
82− azimuth.azimuth.getPoint(contracts, point)
83− .then(details => addToPointCache({ [point]: details }))
84− })
75+ azimuth.azimuth
76+ .getPoint(contracts, point)
77+ .then(details => addToPointCache({ [point]: details }));
78+ });
8579 }
8680
8781 render() {
88− const { setPointCursor, pushRoute } = this.props
89− const { pointCache } = this.props
90−
91− const { points, incoming, managing, voting, spawning, loading } = this.state
92− const { eclipticOwner } = this.state
93−
94− const lookupPointButton =
82+ const { setPointCursor, pushRoute } = this.props;
83+ const { pointCache } = this.props;
84+
85+ const {
86+ points,
87+ incoming,
88+ managing,
89+ voting,
90+ spawning,
91+ loading,
92+ } = this.state;
93+ const { eclipticOwner } = this.state;
94+
95+ const lookupPointButton = (
9596 <Row>
9697 <Col>
9798 <Button
9899 prop-type={'link'}
99100 prop-size={'md'}
100− onClick={ () => pushRoute(ROUTE_NAMES.VIEW_SHIP) }
101− >
102− { 'View a point →' }
101+ onClick={() => pushRoute(ROUTE_NAMES.VIEW_SHIP)}>
102+ {'View a point →'}
103103 </Button>
104− <P>
105− { "View a point on Azimuth." }
106− </P>
104+ <P>{'View a point on Azimuth.'}</P>
107105 </Col>
108106 </Row>
107+ );
109108
110109 const createGalaxyButton =
111− eclipticOwner === false
112− ? <div />
113− : <Row>
110+ eclipticOwner === false ? (
111+ <div />
112+ ) : (
113+ <Row>
114114 <Col>
115115 <Button
116116 prop-type={'link'}
117117 prop-size={'md'}
118− onClick={ () => pushRoute(ROUTE_NAMES.CREATE_GALAXY) }
119− >
120− { 'Create a galaxy →' }
118+ onClick={() => pushRoute(ROUTE_NAMES.CREATE_GALAXY)}>
119+ {'Create a galaxy →'}
121120 </Button>
122121 <P>
123− { "You have the authority to create a new Galaxy and you can do so here." }
122+ {
123+ 'You have the authority to create a new Galaxy and you can do so here.'
124+ }
124125 </P>
125126 </Col>
126127 </Row>
128+ );
127129
128130 const outgoing = points.filter(point =>
129131 hasTransferProxy(pointCache, point)
130− )
132+ );
131133
132134 const outgoingPoints =
133− outgoing.length !== 0
134− ? <React.Fragment>
135− <H2>{ 'Outgoing Transfers' }</H2>
135+ outgoing.length !== 0 ? (
136+ <React.Fragment>
137+ <H2>{'Outgoing Transfers'}</H2>
136138 <P>
137− { `You own these points until the recipient accepts the incoming
138− transfer. You may cancel the transfer until accepted.` }
139+ {`You own these points until the recipient accepts the incoming
140+ transfer. You may cancel the transfer until accepted.`}
139141 </P>
140142 <PointList
141− setPointCursor={ setPointCursor }
142− routeHandler={ pushRoute }
143− points={ outgoing }
143+ setPointCursor={setPointCursor}
144+ routeHandler={pushRoute}
145+ points={outgoing}
144146 />
145147 </React.Fragment>
146− : <div />
148+ ) : (
149+ <div />
150+ );
147151
148152 const incomingPoints =
149− incoming.length !== 0
150− ? <React.Fragment>
151− <H2>{ 'Incoming Transfers' }</H2>
153+ incoming.length !== 0 ? (
154+ <React.Fragment>
155+ <H2>{'Incoming Transfers'}</H2>
152156 <P>
153− { `You do not own these points until you accept the incoming transfer.
154− You may reject any incoming transfers.` }
157+ {`You do not own these points until you accept the incoming transfer.
158+ You may reject any incoming transfers.`}
155159 </P>
156160 <PointList
157− setPointCursor={ setPointCursor }
158− routeHandler={ pushRoute }
159− points={ incoming }
161+ setPointCursor={setPointCursor}
162+ routeHandler={pushRoute}
163+ points={incoming}
160164 />
161165 </React.Fragment>
162− : <div />
166+ ) : (
167+ <div />
168+ );
163169
164170 const managingPoints =
165− managing.length !== 0
166− ? <React.Fragment>
167− <H2>{ 'You Are a Management Proxy For' }</H2>
171+ managing.length !== 0 ? (
172+ <React.Fragment>
173+ <H2>{'You Are a Management Proxy For'}</H2>
168174 <P>
169− { `You can configure or set network keys and conduct sponsorship
170− related operations for these points.` }
175+ {`You can configure or set network keys and conduct sponsorship
176+ related operations for these points.`}
171177 </P>
172178 <PointList
173− setPointCursor={ setPointCursor }
174− routeHandler={ pushRoute }
175− points={ managing }
179+ setPointCursor={setPointCursor}
180+ routeHandler={pushRoute}
181+ points={managing}
176182 />
177183 </React.Fragment>
178− : <div />
184+ ) : (
185+ <div />
186+ );
179187
180188 const votingPoints =
181− voting.length !== 0
182− ? <React.Fragment>
183− <H2>{ 'You Are a Voting Proxy For' }</H2>
189+ voting.length !== 0 ? (
190+ <React.Fragment>
191+ <H2>{'You Are a Voting Proxy For'}</H2>
184192 <P>
185− { `Since you are part of the Galactic Senate, you can cast votes on
186− new Azimuth proposals on behalf of these points.` }
193+ {`Since you are part of the Galactic Senate, you can cast votes on
194+ new Azimuth proposals on behalf of these points.`}
187195 </P>
188196 <PointList
189− setPointCursor={ setPointCursor }
190− routeHandler={ pushRoute }
191− points={ voting }
197+ setPointCursor={setPointCursor}
198+ routeHandler={pushRoute}
199+ points={voting}
192200 />
193201 </React.Fragment>
194− : <div />
202+ ) : (
203+ <div />
204+ );
195205
196206 const spawningPoints =
197− spawning.length !== 0
198− ? <React.Fragment>
199− <H2>{ 'You Are a Spawn Proxy For' }</H2>
200− <P>
201− { `You can create new child ships under these points.` }
202− </P>
207+ spawning.length !== 0 ? (
208+ <React.Fragment>
209+ <H2>{'You Are a Spawn Proxy For'}</H2>
210+ <P>{`You can create new child ships under these points.`}</P>
203211 <PointList
204− setPointCursor={ setPointCursor }
205− routeHandler={ pushRoute }
206− points={ spawning }
212+ setPointCursor={setPointCursor}
213+ routeHandler={pushRoute}
214+ points={spawning}
207215 />
208216 </React.Fragment>
209− : <div />
217+ ) : (
218+ <div />
219+ );
210220
211221 return (
212222 <Row>
213223 <Col>
214− <H1>{ 'Points' }</H1>
224+ <H1>{'Points'}</H1>
215225
216226 <P>{`A point is an identity on the Ethereum blockchain.
217227 Points declare keys for ships on the Arvo network.`}</P>
218228
229+ {lookupPointButton}
219230
220− { lookupPointButton }
221−
222− { createGalaxyButton }
231+ {createGalaxyButton}
223232
224− { incomingPoints }
233+ {incomingPoints}
225234
226− { outgoingPoints }
235+ {outgoingPoints}
227236
228− <H2>{ 'Your Points' }</H2>
237+ <H2>{'Your Points'}</H2>
229238
230− <PointList
231− setPointCursor={ setPointCursor }
232− routeHandler={ pushRoute }
233− points={ points }
234− loading={ loading }
235− />
236−
237− { managingPoints }
239+ <PointList
240+ setPointCursor={setPointCursor}
241+ routeHandler={pushRoute}
242+ points={points}
243+ loading={loading}
244+ />
238245
239− { votingPoints }
246+ {managingPoints}
240247
241− { spawningPoints }
248+ {votingPoints}
242249
250+ {spawningPoints}
243251 </Col>
244252 </Row>
245− )
253+ );
246254 }
247255}
248256
249−export default Points
257+export default Points;
src/bridge/views/PrivateKey.js+51−56
@@ -1,83 +1,78 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import { Button } from '../components/Base'
4−import { RequiredInput, InnerLabel, InputCaption } from '../components/Base'
5−import { Row, Col, H1 } from '../components/Base'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import { Button } from '../components/Base';
4+import { RequiredInput, InnerLabel, InputCaption } from '../components/Base';
5+import { Row, Col, H1 } from '../components/Base';
66
7−import { ROUTE_NAMES } from '../lib/router'
8−import { EthereumWallet } from '../lib/wallet'
7+import { ROUTE_NAMES } from '../lib/router';
8+import { EthereumWallet } from '../lib/wallet';
99
1010class PrivateKey extends React.Component {
11−
1211 constructor(props) {
13− super(props)
12+ super(props);
1413
1514 this.state = {
1615 privateKey: '',
17− }
16+ };
1817
19− this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this)
18+ this.handlePrivateKeyInput = this.handlePrivateKeyInput.bind(this);
2019 }
2120
2221 handlePrivateKeyInput(privateKey) {
23− this.setState({ privateKey })
24− this.constructWallet(privateKey)
22+ this.setState({ privateKey });
23+ this.constructWallet(privateKey);
2524 }
2625
2726 constructWallet(privateKey) {
28− const { setWallet } = this.props
27+ const { setWallet } = this.props;
2928 if (/^[0-9A-Fa-f]{64}$/g.test(privateKey) === true) {
30− const sec = Buffer.from(privateKey, 'hex')
31− const wallet = new EthereumWallet(sec)
32− setWallet(Maybe.Just(wallet))
29+ const sec = Buffer.from(privateKey, 'hex');
30+ const wallet = new EthereumWallet(sec);
31+ setWallet(Maybe.Just(wallet));
3332 } else {
34− setWallet(Maybe.Nothing())
33+ setWallet(Maybe.Nothing());
3534 }
3635 }
3736
3837 render() {
39− const { pushRoute, popRoute, wallet } = this.props
40− const { privateKey } = this.state
38+ const { pushRoute, popRoute, wallet } = this.props;
39+ const { privateKey } = this.state;
4140
4241 return (
43− <Row>
44− <Col className={'measure-md'}>
45− <H1 className={'mb-4'}>{ 'Enter Your Private Key' }</H1>
46− <InputCaption>
47− { `Please enter your raw Ethereum private key here.` }
48− </InputCaption>
49−
50− <RequiredInput
51− className='pt-8 mt-8'
52− prop-size='md'
53− prop-format='innerLabel'
54− type='password'
55− name='privateKey'
56− onChange={ this.handlePrivateKeyInput }
57− value={ privateKey }
58− autocomplete='off'
59− autoFocus>
60− <InnerLabel>{'Private Key'}</InnerLabel>
61− </RequiredInput>
62−
63− <Button
64− className={'mt-10'}
65− prop-size={ 'wide lg' }
66− disabled={ Maybe.Nothing.hasInstance(wallet) }
67− onClick={ () => {
68− popRoute()
69− pushRoute(ROUTE_NAMES.SHIPS)
70− }
71− }
72− >
73− { 'Continue →' }
74− </Button>
75− </Col>
76− </Row>
42+ <Row>
43+ <Col className={'measure-md'}>
44+ <H1 className={'mb-4'}>{'Enter Your Private Key'}</H1>
45+ <InputCaption>
46+ {`Please enter your raw Ethereum private key here.`}
47+ </InputCaption>
7748
49+ <RequiredInput
50+ className="pt-8 mt-8"
51+ prop-size="md"
52+ prop-format="innerLabel"
53+ type="password"
54+ name="privateKey"
55+ onChange={this.handlePrivateKeyInput}
56+ value={privateKey}
57+ autocomplete="off"
58+ autoFocus>
59+ <InnerLabel>{'Private Key'}</InnerLabel>
60+ </RequiredInput>
7861
79− )
62+ <Button
63+ className={'mt-10'}
64+ prop-size={'wide lg'}
65+ disabled={Maybe.Nothing.hasInstance(wallet)}
66+ onClick={() => {
67+ popRoute();
68+ pushRoute(ROUTE_NAMES.SHIPS);
69+ }}>
70+ {'Continue →'}
71+ </Button>
72+ </Col>
73+ </Row>
74+ );
8075 }
8176}
8277
83−export default PrivateKey
78+export default PrivateKey;
src/bridge/views/SentTransaction.js+104−113
@@ -1,165 +1,158 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import * as need from '../lib/need'
4−import { Row, Col, H1, H3, P, Warning, Anchor } from '../components/Base'
5−import { Button } from '../components/Base'
6−
7−import { ROUTE_NAMES } from '../lib/router'
8−import { BRIDGE_ERROR, renderTxnError } from '../lib/error'
9−import { NETWORK_NAMES } from '../lib/network'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import * as need from '../lib/need';
4+import { Row, Col, H1, H3, P, Warning, Anchor } from '../components/Base';
5+import { Button } from '../components/Base';
106
7+import { ROUTE_NAMES } from '../lib/router';
8+import { BRIDGE_ERROR, renderTxnError } from '../lib/error';
9+import { NETWORK_NAMES } from '../lib/network';
1110
1211class Success extends React.Component {
13−
1412 constructor(props) {
15− super(props)
13+ super(props);
1614
1715 this.state = {
1816 pending: '.',
19− interval: null
20− }
17+ interval: null,
18+ };
2119 }
2220
2321 componentDidMount() {
24− const nextDot = {'.': '..',
25− '..': '...',
26− '...': '.'}
22+ const nextDot = { '.': '..', '..': '...', '...': '.' };
2723
2824 const interval = setInterval(() => {
29− this.setState(({ pending }) => ({pending: nextDot[pending]}))
30− }, 1000)
31− this.setState({interval: interval})
25+ this.setState(({ pending }) => ({ pending: nextDot[pending] }));
26+ }, 1000);
27+ this.setState({ interval: interval });
3228 }
3329
3430 componentWillUnmount() {
35− clearInterval(this.state.interval)
31+ clearInterval(this.state.interval);
3632 }
3733
3834 render() {
39−
40− const { props, state } = this
41− const { networkType, hash, txnConfirmations } = props
42− const { pending } = state
35+ const { props, state } = this;
36+ const { networkType, hash, txnConfirmations } = props;
37+ const { pending } = state;
4338
4439 const esvisible =
45− networkType === NETWORK_NAMES.ROPSTEN ||
46− networkType === NETWORK_NAMES.MAINNET
40+ networkType === NETWORK_NAMES.ROPSTEN ||
41+ networkType === NETWORK_NAMES.MAINNET;
4742
4843 const esdomain =
49− networkType === NETWORK_NAMES.ROPSTEN
50− ? "ropsten.etherscan.io"
51− : "etherscan.io"
44+ networkType === NETWORK_NAMES.ROPSTEN
45+ ? 'ropsten.etherscan.io'
46+ : 'etherscan.io';
5247
5348 const esmessage =
54− esvisible === true
55− ? "If you’d like to keep track of it, click the Etherscan link below."
56− : ''
49+ esvisible === true
50+ ? 'If you’d like to keep track of it, click the Etherscan link below.'
51+ : '';
5752
5853 const esanchor =
59− esvisible === false
60− ? null
61− : <Anchor
54+ esvisible === false ? null : (
55+ <Anchor
6256 className={'mb-4 mt-1'}
6357 prop-size={'sm'}
6458 target={'_blank'}
6559 href={`https://${esdomain}/tx/${hash}`}>
66− {'View on Etherscan ↗'}
67− </Anchor>
60+ {'View on Etherscan ↗'}
61+ </Anchor>
62+ );
6863
69− const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(0)
64+ const confirmations = Maybe.fromNullable(txnConfirmations[hash]).getOrElse(
65+ 0
66+ );
7067
71− const requiredConfirmations = 1
68+ const requiredConfirmations = 1;
7269
73− const status = confirmations < requiredConfirmations ?
74− `Pending${pending}` : `Confirmed! (x${confirmations} confirmations)!`
70+ const status =
71+ confirmations < requiredConfirmations
72+ ? `Pending${pending}`
73+ : `Confirmed! (x${confirmations} confirmations)!`;
7574
7675 return (
7776 <Row>
7877 <Col>
79− <H1>{ 'Your Transaction was Sent' }</H1>
80−
81− <P>
82− {
83− `We sent your transaction to the chain. It can take some time to
84− execute, especially if the network is busy. ${esmessage}`
85− }
86− </P>
78+ <H1>{'Your Transaction was Sent'}</H1>
8779
88− <H3>{ 'Transaction Hash' }</H3>
8980 <P>
90− { hash }
81+ {`We sent your transaction to the chain. It can take some time to
82+ execute, especially if the network is busy. ${esmessage}`}
9183 </P>
9284
93− <H3>{ 'Transaction Status' }</H3>
94− <P>
95− { status }
96− </P>
97− <P>
98− { esanchor }
99− </P>
85+ <H3>{'Transaction Hash'}</H3>
86+ <P>{hash}</P>
10087
88+ <H3>{'Transaction Status'}</H3>
89+ <P>{status}</P>
90+ <P>{esanchor}</P>
10191 </Col>
10292 </Row>
103− )
93+ );
10494 }
10595}
10696
107−const Failure = (props) =>
108−
109− <Row>
110− <Col>
111− <H1>{ 'Error!' }</H1>
112−
113−
114− <Warning>
115− <H3>
116− {
117− 'There was an error sending your transaction.'
118− }
119− </H3>
120− { renderTxnError(props.web3, props.message) }
121− </Warning>
122− </Col>
123− </Row>
124−
125−const SentTransaction = (props) => {
126− const { txnHashCursor, networkType, popRoute, pushRoute, txnConfirmations} = props
127− const { setPointCursor, pointCursor } = props
128−
129− const promptKeyfile = props.routeData && props.routeData.promptKeyfile
97+const Failure = props => (
98+ <Row>
99+ <Col>
100+ <H1>{'Error!'}</H1>
101+
102+ <Warning>
103+ <H3>{'There was an error sending your transaction.'}</H3>
104+ {renderTxnError(props.web3, props.message)}
105+ </Warning>
106+ </Col>
107+ </Row>
108+);
109+
110+const SentTransaction = props => {
111+ const {
112+ txnHashCursor,
113+ networkType,
114+ popRoute,
115+ pushRoute,
116+ txnConfirmations,
117+ } = props;
118+ const { setPointCursor, pointCursor } = props;
119+
120+ const promptKeyfile = props.routeData && props.routeData.promptKeyfile;
130121
131122 const w3 = need.web3(props);
132123
133124 const result = txnHashCursor.matchWith({
134− Nothing: _ => { throw BRIDGE_ERROR.MISSING_TXN },
135− Just: res => res.value
136− })
125+ Nothing: _ => {
126+ throw BRIDGE_ERROR.MISSING_TXN;
127+ },
128+ Just: res => res.value,
129+ });
137130
138131 const body = result.matchWith({
139− Error: message => <Failure web3={ w3 } message={ message.value } />,
140− Ok: hash =>
132+ Error: message => <Failure web3={w3} message={message.value} />,
133+ Ok: hash => (
141134 <Success
142− hash={ hash.value }
143− networkType={ networkType }
144− txnConfirmations={ txnConfirmations }
135+ hash={hash.value}
136+ networkType={networkType}
137+ txnConfirmations={txnConfirmations}
145138 />
146− })
139+ ),
140+ });
147141
148− const ok =
142+ const ok = (
149143 <Row>
150144 <Col>
151145 <Button
152146 prop-type={'link'}
153− onClick={
154− () => {
155− setPointCursor(pointCursor)
156− popRoute()
157− }
158− }>
159− { 'Ok →' }
147+ onClick={() => {
148+ setPointCursor(pointCursor);
149+ popRoute();
150+ }}>
151+ {'Ok →'}
160152 </Button>
161153 </Col>
162154 </Row>
155+ );
163156
164157 let keyfile;
165158
@@ -169,26 +162,24 @@ const SentTransaction = (props) => {
169162 <Col>
170163 <Button
171164 prop-type={'link'}
172− onClick={
173− () => {
174− popRoute()
175− pushRoute(ROUTE_NAMES.GEN_KEYFILE)
176− }
177− }>
178− { 'Download Keyfile →' }
165+ onClick={() => {
166+ popRoute();
167+ pushRoute(ROUTE_NAMES.GEN_KEYFILE);
168+ }}>
169+ {'Download Keyfile →'}
179170 </Button>
180171 </Col>
181172 </Row>
182− )
173+ );
183174 }
184175
185176 return (
186177 <div>
187− { body }
188− { ok }
189− { keyfile }
178+ {body}
179+ {ok}
180+ {keyfile}
190181 </div>
191− )
192−}
182+ );
183+};
193184
194−export default SentTransaction
185+export default SentTransaction;
src/bridge/views/SetKeys.js+81−70
@@ -1,20 +1,20 @@
1−import React from 'react'
2−import Maybe from 'folktale/maybe'
3−import * as azimuth from 'azimuth-js'
4−import * as ob from 'urbit-ob'
5−import * as need from '../lib/need'
6−import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base'
1+import React from 'react';
2+import Maybe from 'folktale/maybe';
3+import * as azimuth from 'azimuth-js';
4+import * as ob from 'urbit-ob';
5+import * as need from '../lib/need';
6+import { Row, Col, H1, P, Warning, CheckboxButton } from '../components/Base';
77
8−import StatelessTransaction from '../components/StatelessTransaction'
9−import { attemptSeedDerivation } from '../lib/keys'
8+import StatelessTransaction from '../components/StatelessTransaction';
9+import { attemptSeedDerivation } from '../lib/keys';
1010
11−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
11+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
1212
13−import { addHexPrefix } from '../lib/wallet'
13+import { addHexPrefix } from '../lib/wallet';
1414
1515class SetKeys extends React.Component {
1616 constructor(props) {
17− super(props)
17+ super(props);
1818
1919 const point = need.pointCursor(props);
2020
@@ -27,11 +27,11 @@ class SetKeys extends React.Component {
2727 cryptoSuiteVersion: 1,
2828 discontinuity: false,
2929 isManagementSeed: false,
30− }
30+ };
3131
3232 this.toggleDiscontinuity = this.toggleDiscontinuity.bind(this);
3333 // Transaction
34− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
34+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
3535 }
3636
3737 toggleDiscontinuity() {
@@ -39,39 +39,56 @@ class SetKeys extends React.Component {
3939 }
4040
4141 componentDidMount() {
42− const { props } = this
42+ const { props } = this;
4343
44− this.deriveSeed()
44+ this.deriveSeed();
4545
4646 const addr = need.address(props);
4747
48− this.determineManagementSeed(props.contracts.value, addr)
48+ this.determineManagementSeed(props.contracts.value, addr);
4949 }
5050
5151 async determineManagementSeed(ctrcs, addr) {
52− const managing =
53− await azimuth.azimuth.getManagerFor(ctrcs, addr)
52+ const managing = await azimuth.azimuth.getManagerFor(ctrcs, addr);
5453
5554 this.setState({
56− isManagementSeed: managing.length !== 0
57− })
55+ isManagementSeed: managing.length !== 0,
56+ });
5857 }
5958
6059 //TODO use web3.utils.randomHex when it gets fixed, see web3.js#1490
6160 randomHex(len) {
62− let hex = "";
61+ let hex = '';
6362
6463 for (var i = 0; i < len; i++) {
65− hex = hex + ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B',
66− 'C', 'D', 'E', 'F'][Math.floor(Math.random() * 16)]
64+ hex =
65+ hex +
66+ [
67+ '0',
68+ '1',
69+ '2',
70+ '3',
71+ '4',
72+ '5',
73+ '6',
74+ '7',
75+ '8',
76+ '9',
77+ 'A',
78+ 'B',
79+ 'C',
80+ 'D',
81+ 'E',
82+ 'F',
83+ ][Math.floor(Math.random() * 16)];
6784 }
6885
6986 return hex;
7087 }
7188
7289 async deriveSeed() {
73− const next = true
74− let seed = await attemptSeedDerivation(next, this.props)
90+ const next = true;
91+ let seed = await attemptSeedDerivation(next, this.props);
7592 let nondeterministicSeed = false;
7693
7794 if (seed.getOrElse('') === '') {
@@ -81,25 +98,25 @@ class SetKeys extends React.Component {
8198
8299 this.setState({
83100 networkSeed: seed.getOrElse(''),
84− nondeterministicSeed: nondeterministicSeed
85− })
101+ nondeterministicSeed: nondeterministicSeed,
102+ });
86103 }
87104
88105 createUnsignedTxn() {
89− const { state, props } = this
106+ const { state, props } = this;
90107
91108 const validContracts = need.contracts(props);
92109
93110 const validPoint = need.pointCursor(props);
94111
95− const hexRegExp = /[0-9A-Fa-f]{64}/g
112+ const hexRegExp = /[0-9A-Fa-f]{64}/g;
96113
97114 if (hexRegExp.test(state.networkSeed)) {
98115 // derive network keys
99− const pair = kg.deriveNetworkKeys(state.networkSeed)
116+ const pair = kg.deriveNetworkKeys(state.networkSeed);
100117
101− const pencr = addHexPrefix(pair.crypt.public)
102− const pauth = addHexPrefix(pair.auth.public)
118+ const pencr = addHexPrefix(pair.crypt.public);
119+ const pauth = addHexPrefix(pair.auth.public);
103120
104121 const txn = azimuth.ecliptic.configureKeys(
105122 validContracts,
@@ -108,20 +125,19 @@ class SetKeys extends React.Component {
108125 pauth,
109126 1,
110127 state.discontinuity
111− )
128+ );
112129
113− return Maybe.Just(txn)
130+ return Maybe.Just(txn);
114131 }
115132
116− return Maybe.Nothing()
133+ return Maybe.Nothing();
117134 }
118135
119136 render() {
120− const { props, state } = this
137+ const { props, state } = this;
121138
122139 const canGenerate =
123− state.networkSeed.length === 64
124− && state.networkSeed.length === 64
140+ state.networkSeed.length === 64 && state.networkSeed.length === 64;
125141
126142 const pointDetails = need.fromPointCache(props, state.point);
127143
@@ -129,59 +145,54 @@ class SetKeys extends React.Component {
129145 <Row>
130146 <Col>
131147 <H1>
132− { 'Set Network Keys For ' } <code>{ `${ob.patp(state.point)}` }</code>
148+ {'Set Network Keys For '} <code>{`${ob.patp(state.point)}`}</code>
133149 </H1>
134150
135151 <P className="mt-10">
136− {
137− `Set new authentication and encryption keys for your Arvo ship.`
138− }
152+ {`Set new authentication and encryption keys for your Arvo ship.`}
139153 </P>
140154
141− { state.nondeterministicSeed &&
155+ {state.nondeterministicSeed && (
142156 <Warning>
143157 <h3 className={'mb-2'}>{'Warning'}</h3>
144− {
145− `Your network seed could not be derived automatically. We've
158+ {`Your network seed could not be derived automatically. We've
146159 generated a random one for you, so you must download your Arvo
147− keyfile during this session after setting your keys.`
148− }
160+ keyfile during this session after setting your keys.`}
161+ </Warning>
162+ )}
163+
164+ {pointDetails.keyRevisionNumber === 0 ? (
165+ <Warning>
166+ <h3 className={'mb-2'}>{'Warning'}</h3>
167+ {'Once these keys have been set, your point is considered ' +
168+ "'linked'. This operation cannot be undone."}
149169 </Warning>
150− }
151−
152− { pointDetails.keyRevisionNumber === 0
153− ? <Warning>
154− <h3 className={'mb-2'}>{'Warning'}</h3>
155− {
156− 'Once these keys have been set, your point is considered ' +
157− "'linked'. This operation cannot be undone."
158− }
159− </Warning>
160− : <div /> }
170+ ) : (
171+ <div />
172+ )}
161173
162174 <CheckboxButton
163− className='mt-8'
164− onToggle={ this.toggleDiscontinuity }
165− state={ state.discontinuity }
175+ className="mt-8"
176+ onToggle={this.toggleDiscontinuity}
177+ state={state.discontinuity}
166178 label={`I have lost important off-chain data relating to this point
167179 and need to do a hard reset.
168− (For example, rebooting an Arvo ship.)`}
169− ></CheckboxButton>
180+ (For example, rebooting an Arvo ship.)`}></CheckboxButton>
170181
171182 <StatelessTransaction
172183 // Upper scope
173184 {...props}
174185 // Tx
175− canGenerate={ canGenerate }
186+ canGenerate={canGenerate}
176187 createUnsignedTxn={this.createUnsignedTxn}
177− networkSeed={ state.networkSeed }
178− newRevision={ (parseInt(pointDetails.keyRevisionNumber) + 1) }
179− setNetworkSeedCache={ props.setNetworkSeedCache } />
180−
188+ networkSeed={state.networkSeed}
189+ newRevision={parseInt(pointDetails.keyRevisionNumber) + 1}
190+ setNetworkSeedCache={props.setNetworkSeedCache}
191+ />
181192 </Col>
182193 </Row>
183− )
194+ );
184195 }
185196}
186197
187−export default SetKeys
198+export default SetKeys;
src/bridge/views/SetProxy.js+97−102
@@ -1,171 +1,169 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import * as need from '../lib/need'
4−import * as azimuth from 'azimuth-js'
5−import { Row, Col, H1, P, InnerLabel, ShowBlockie, Anchor, HorizontalSelector } from '../components/Base'
6−import { AddressInput } from '../components/Base'
7−import * as ob from 'urbit-ob'
8−
9−import { PROXY_TYPE, renderProxyType } from '../lib/proxy'
10−
11−import { NETWORK_NAMES } from '../lib/network'
12−
13−import StatelessTransaction from '../components/StatelessTransaction'
14−
15−import { isValidAddress } from '../lib/wallet'
16−
17−const SetManagementProxy = (props) =>
18− <SetProxy
19− { ...props }
20− proxyType={ PROXY_TYPE.MANAGEMENT_PROXY }
21− />
22−
23−const SetSpawnProxy = (props) =>
24− <SetProxy
25− { ...props }
26− proxyType={ PROXY_TYPE.SPAWN_PROXY }
27− />
28−
29−const SetTransferProxy = (props) =>
30− <SetProxy
31− { ...props }
32− proxyType={ PROXY_TYPE.TRANSFER_PROXY }
33− />
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import * as need from '../lib/need';
4+import * as azimuth from 'azimuth-js';
5+import {
6+ Row,
7+ Col,
8+ H1,
9+ P,
10+ InnerLabel,
11+ ShowBlockie,
12+ Anchor,
13+ HorizontalSelector,
14+} from '../components/Base';
15+import { AddressInput } from '../components/Base';
16+import * as ob from 'urbit-ob';
17+
18+import { PROXY_TYPE, renderProxyType } from '../lib/proxy';
19+
20+import { NETWORK_NAMES } from '../lib/network';
21+
22+import StatelessTransaction from '../components/StatelessTransaction';
23+
24+import { isValidAddress } from '../lib/wallet';
25+
26+const SetManagementProxy = props => (
27+ <SetProxy {...props} proxyType={PROXY_TYPE.MANAGEMENT_PROXY} />
28+);
29+
30+const SetSpawnProxy = props => (
31+ <SetProxy {...props} proxyType={PROXY_TYPE.SPAWN_PROXY} />
32+);
33+
34+const SetTransferProxy = props => (
35+ <SetProxy {...props} proxyType={PROXY_TYPE.TRANSFER_PROXY} />
36+);
3437
3538class SetProxy extends React.Component {
3639 constructor(props) {
37− super(props)
40+ super(props);
3841
3942 const issuingPoint = need.pointCursor(props);
4043
4144 this.state = {
4245 proxyAddress: '',
4346 issuingPoint: issuingPoint,
44− setProxy: true
45− }
47+ setProxy: true,
48+ };
4649
4750 this.statelessRef = React.createRef();
4851 this.handleSetUnset = this.handleSetUnset.bind(this);
49− this.handleAddressInput = this.handleAddressInput.bind(this)
50− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
52+ this.handleAddressInput = this.handleAddressInput.bind(this);
53+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
5154 }
5255
5356 handleSetUnset(selected) {
54− const set = (selected === 'set');
57+ const set = selected === 'set';
5558 this.setState({
5659 setProxy: set,
57− proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000'
60+ proxyAddress: set ? '' : '0x0000000000000000000000000000000000000000',
5861 });
5962 }
6063
6164 handleAddressInput(proxyAddress) {
62− this.setState({ proxyAddress })
65+ this.setState({ proxyAddress });
6366
64− this.statelessRef.current.clearTxn()
67+ this.statelessRef.current.clearTxn();
6568 }
6669
6770 createUnsignedTxn(proxyAddress) {
68− const { state, props } = this
71+ const { state, props } = this;
6972
7073 const validContracts = need.contracts(props);
7174
7275 const validPoint = need.pointCursor(props);
7376
74− const txArgs = [
75− validContracts,
76− validPoint,
77− state.proxyAddress
78− ]
77+ const txArgs = [validContracts, validPoint, state.proxyAddress];
7978
8079 if (props.proxyType === PROXY_TYPE.TRANSFER_PROXY) {
81− return Maybe.Just(azimuth.ecliptic.setTransferProxy(...txArgs))
80+ return Maybe.Just(azimuth.ecliptic.setTransferProxy(...txArgs));
8281 }
8382
8483 if (props.proxyType === PROXY_TYPE.SPAWN_PROXY) {
85− return Maybe.Just(azimuth.ecliptic.setSpawnProxy(...txArgs))
84+ return Maybe.Just(azimuth.ecliptic.setSpawnProxy(...txArgs));
8685 }
8786
8887 if (props.proxyType === PROXY_TYPE.MANAGEMENT_PROXY) {
89− return Maybe.Just(azimuth.ecliptic.setManagementProxy(...txArgs))
88+ return Maybe.Just(azimuth.ecliptic.setManagementProxy(...txArgs));
9089 }
9190
92− return Maybe.Nothing()
93−
91+ return Maybe.Nothing();
9492 }
9593
9694 render() {
97− const { props, state } = this
95+ const { props, state } = this;
9896
99− const renderedProxyType = renderProxyType(props.proxyType)
100− const validAddress = isValidAddress(state.proxyAddress)
101− const canGenerate = validAddress === true
97+ const renderedProxyType = renderProxyType(props.proxyType);
98+ const validAddress = isValidAddress(state.proxyAddress);
99+ const canGenerate = validAddress === true;
102100
103101 const esvisible =
104− props.networkType === NETWORK_NAMES.ROPSTEN ||
105− props.networkType === NETWORK_NAMES.MAINNET
102+ props.networkType === NETWORK_NAMES.ROPSTEN ||
103+ props.networkType === NETWORK_NAMES.MAINNET;
106104
107105 const esdomain =
108− props.networkType === NETWORK_NAMES.ROPSTEN
109− ? "ropsten.etherscan.io"
110− : "etherscan.io"
106+ props.networkType === NETWORK_NAMES.ROPSTEN
107+ ? 'ropsten.etherscan.io'
108+ : 'etherscan.io';
111109
112110 const ucFirst = w => w.charAt(0).toUpperCase() + w.slice(1);
113111
114112 const setUnset = [
115113 { title: 'Set', value: 'set' },
116− { title: 'Unset', value: 'unset' }
114+ { title: 'Unset', value: 'unset' },
117115 ];
118116
119117 const titleVerb = this.state.setProxy ? 'Set' : 'Unset';
120118
121119 let addressInput = null;
122120 if (this.state.setProxy) {
123− addressInput = (<>
124− <P className='mt-8'>
125− {
126− 'Please provide an Ethereum address to act as the ' +
127− `${renderedProxyType} proxy. You can also use Wallet ` +
128− 'Generator to generate a keypair.'
129− }
130− </P>
131−
132− <AddressInput
133− className='mono mt-8'
134− prop-size='lg'
135− prop-format='innerLabel'
136− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
137− value={ state.proxyAddress }
138− onChange={ v => this.handleAddressInput(v) }>
139− <InnerLabel>{ 'Proxy Address' }</InnerLabel>
140− <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
141− </AddressInput>
142−
143− <Anchor
144− className={'mt-1'}
145− prop-size={'sm'}
146− prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
147− target={'_blank'}
148− href={`https://${esdomain}/address/${state.proxyAddress}`}>
121+ addressInput = (
122+ <>
123+ <P className="mt-8">
124+ {'Please provide an Ethereum address to act as the ' +
125+ `${renderedProxyType} proxy. You can also use Wallet ` +
126+ 'Generator to generate a keypair.'}
127+ </P>
128+
129+ <AddressInput
130+ className="mono mt-8"
131+ prop-size="lg"
132+ prop-format="innerLabel"
133+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
134+ value={state.proxyAddress}
135+ onChange={v => this.handleAddressInput(v)}>
136+ <InnerLabel>{'Proxy Address'}</InnerLabel>
137+ <ShowBlockie className={'mt-1'} address={state.proxyAddress} />
138+ </AddressInput>
139+
140+ <Anchor
141+ className={'mt-1'}
142+ prop-size={'sm'}
143+ prop-disabled={!isValidAddress(state.proxyAddress) || !esvisible}
144+ target={'_blank'}
145+ href={`https://${esdomain}/address/${state.proxyAddress}`}>
149146 {'View on Etherscan ↗'}
150− </Anchor>
151− </>)
147+ </Anchor>
148+ </>
149+ );
152150 }
153151
154152 return (
155153 <Row>
156154 <Col>
157155 <H1>
158− { `${titleVerb} ${ucFirst(renderedProxyType)} Proxy For ` }
159− <code>{ `${ob.patp(state.issuingPoint)}` }</code>
156+ {`${titleVerb} ${ucFirst(renderedProxyType)} Proxy For `}
157+ <code>{`${ob.patp(state.issuingPoint)}`}</code>
160158 </H1>
161159
162160 <HorizontalSelector
163161 options={setUnset}
164162 onChange={this.handleSetUnset}
165− className='mt-8'
163+ className="mt-8"
166164 />
167165
168− { addressInput }
166+ {addressInput}
169167
170168 <StatelessTransaction
171169 // Upper scope
@@ -173,15 +171,12 @@ class SetProxy extends React.Component {
173171 // Other
174172 ref={this.statelessRef}
175173 createUnsignedTxn={this.createUnsignedTxn}
176− canGenerate={ canGenerate } />
174+ canGenerate={canGenerate}
175+ />
177176 </Col>
178177 </Row>
179− )
178+ );
180179 }
181180}
182181
183−export {
184− SetManagementProxy,
185− SetSpawnProxy,
186− SetTransferProxy
187−}
182+export { SetManagementProxy, SetSpawnProxy, SetTransferProxy };
src/bridge/views/Shards.js+111−117
@@ -1,86 +1,85 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import { Button } from '../components/Base'
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import { Button } from '../components/Base';
44import {
55 InnerLabel,
66 ValidatedSigil,
77 PointInput,
88 ShardInput,
99 InputCaption,
10− Input
11− } from '../components/Base'
12−import { Row, Col, H1, P } from '../components/Base'
13−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
14−import * as ob from 'urbit-ob'
15−
16−import { ROUTE_NAMES } from '../lib/router'
17−import { urbitWalletFromTicket } from '../lib/wallet'
18−
19−const placeholder = (len) => {
20− let bytes = window.crypto.getRandomValues(new Uint8Array(len))
21− let hex = bytes.reduce((acc, byt) =>
22− acc + byt.toString(16).padStart(2, '0'),
10+ Input,
11+} from '../components/Base';
12+import { Row, Col, H1, P } from '../components/Base';
13+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
14+import * as ob from 'urbit-ob';
15+
16+import { ROUTE_NAMES } from '../lib/router';
17+import { urbitWalletFromTicket } from '../lib/wallet';
18+
19+const placeholder = len => {
20+ let bytes = window.crypto.getRandomValues(new Uint8Array(len));
21+ let hex = bytes.reduce(
22+ (acc, byt) => acc + byt.toString(16).padStart(2, '0'),
2323 ''
24− )
25− return ob.hex2patq(hex)
26−}
24+ );
25+ return ob.hex2patq(hex);
26+};
2727
2828const SHARDS = {
2929 SHARD1: Symbol('SHARD1'),
3030 SHARD2: Symbol('SHARD2'),
31− SHARD3: Symbol('SHARD3')
32−}
31+ SHARD3: Symbol('SHARD3'),
32+};
3333
3434class Shards extends React.Component {
35−
3635 constructor(props) {
37− super(props)
36+ super(props);
3837
3938 this.state = {
4039 shard1: '',
4140 shard2: '',
4241 shard3: '',
4342 passphrase: '',
44− pointName: ''
45− }
43+ pointName: '',
44+ };
4645
47− this.pointPlaceholder = placeholder(4)
48− this.ticketPlaceholder = placeholder(16)
46+ this.pointPlaceholder = placeholder(4);
47+ this.ticketPlaceholder = placeholder(16);
4948
50− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
51− this.handleShardInput = this.handleShardInput.bind(this)
52− this.handlePointNameInput = this.handlePointNameInput.bind(this)
49+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
50+ this.handleShardInput = this.handleShardInput.bind(this);
51+ this.handlePointNameInput = this.handlePointNameInput.bind(this);
5352 }
5453
5554 handleShardInput(shard, input) {
5655 if (shard === SHARDS.SHARD1) {
57− this.setState({ shard1: input })
56+ this.setState({ shard1: input });
5857 } else if (shard === SHARDS.SHARD2) {
59− this.setState({ shard2: input })
58+ this.setState({ shard2: input });
6059 } else if (shard === SHARDS.SHARD3) {
61− this.setState({ shard3: input })
60+ this.setState({ shard3: input });
6261 }
6362 }
6463
6564 handlePassphraseInput(passphrase) {
66− this.setState({ passphrase })
65+ this.setState({ passphrase });
6766 }
6867
6968 handlePointNameInput(pointName) {
7069 if (pointName.length < 15) {
71− this.setState({ pointName })
70+ this.setState({ pointName });
7271 }
7372 }
7473
7574 async walletFromShards(shard1, shard2, shard3, pointName, passphrase) {
76− const s1 = shard1 === '' ? undefined : shard1
77− const s2 = shard2 === '' ? undefined : shard2
78− const s3 = shard3 === '' ? undefined : shard3
75+ const s1 = shard1 === '' ? undefined : shard1;
76+ const s2 = shard2 === '' ? undefined : shard2;
77+ const s3 = shard3 === '' ? undefined : shard3;
7978
80− let ticket = undefined
79+ let ticket = undefined;
8180 try {
82− ticket = kg.combine([s1, s2, s3])
83− } catch(_) {
81+ ticket = kg.combine([s1, s2, s3]);
82+ } catch (_) {
8483 // do nothing
8584 }
8685
@@ -91,101 +90,98 @@ class Shards extends React.Component {
9190 }
9291
9392 render() {
94− const { popRoute, pushRoute, wallet } = this.props
95− const { shard1, shard2, shard3, pointName, passphrase } = this.state
93+ const { popRoute, pushRoute, wallet } = this.props;
94+ const { shard1, shard2, shard3, pointName, passphrase } = this.state;
9695
97− const phPoint = this.pointPlaceholder
98− const phTick = this.ticketPlaceholder
96+ const phPoint = this.pointPlaceholder;
97+ const phTick = this.ticketPlaceholder;
9998
100− const shards = [shard1, shard2, shard3]
101− const ready = shards.filter(x => x !== '').length > 1
99+ const shards = [shard1, shard2, shard3];
100+ const ready = shards.filter(x => x !== '').length > 1;
102101
103102 return (
104− <Row>
105− <Col>
106− <H1>{ 'Authenticate' }</H1>
103+ <Row>
104+ <Col>
105+ <H1>{'Authenticate'}</H1>
107106
108− <P>
109− {
110− `Enter your point and at least two of your three Urbit master
107+ <P>
108+ {`Enter your point and at least two of your three Urbit master
111109 ticket shards here. The index of the input field needs to
112− match the index of the shard.`
113− }
114−
115− </P>
110+ match the index of the shard.`}
111+ </P>
116112
117113 <PointInput
118− className='mono mt-8'
119− prop-size='lg'
120− prop-format='innerLabel'
121− type='text'
114+ className="mono mt-8"
115+ prop-size="lg"
116+ prop-format="innerLabel"
117+ type="text"
122118 autoFocus
123− placeholder={ `e.g. ${phPoint}` }
124− value={ pointName }
125− onChange={ this.handlePointNameInput }>
126− <InnerLabel>{ 'Point' }</InnerLabel>
119+ placeholder={`e.g. ${phPoint}`}
120+ value={pointName}
121+ onChange={this.handlePointNameInput}>
122+ <InnerLabel>{'Point'}</InnerLabel>
127123 <ValidatedSigil
128124 className={'tr-0 mt-05 mr-0 abs'}
129125 patp={pointName}
130126 size={68}
131− margin={8} />
132− </PointInput>
133−
127+ margin={8}
128+ />
129+ </PointInput>
134130
135131 <ShardInput
136− className='mono mt-8'
137− prop-size='md'
138− prop-format='innerLabel'
139− type='text'
140− name='shard1'
141− placeholder={ `e.g. ${phTick}` }
142− value={ shard1 }
143− onChange={ inp => this.handleShardInput(SHARDS.SHARD1, inp) }>
144− <InnerLabel>{ 'Shard 1' }</InnerLabel>
132+ className="mono mt-8"
133+ prop-size="md"
134+ prop-format="innerLabel"
135+ type="text"
136+ name="shard1"
137+ placeholder={`e.g. ${phTick}`}
138+ value={shard1}
139+ onChange={inp => this.handleShardInput(SHARDS.SHARD1, inp)}>
140+ <InnerLabel>{'Shard 1'}</InnerLabel>
145141 </ShardInput>
146142
147143 <ShardInput
148− className='mono mt-8'
149− prop-size='md'
150− prop-format='innerLabel'
151− type='text'
152− name='shard2'
153− placeholder={ `e.g. ${phTick}` }
154− value={ shard2 }
155− onChange={ inp => this.handleShardInput(SHARDS.SHARD2, inp) }>
156− <InnerLabel>{ 'Shard 2' }</InnerLabel>
144+ className="mono mt-8"
145+ prop-size="md"
146+ prop-format="innerLabel"
147+ type="text"
148+ name="shard2"
149+ placeholder={`e.g. ${phTick}`}
150+ value={shard2}
151+ onChange={inp => this.handleShardInput(SHARDS.SHARD2, inp)}>
152+ <InnerLabel>{'Shard 2'}</InnerLabel>
157153 </ShardInput>
158154
159155 <ShardInput
160− className='mono mt-8'
161− prop-size='md'
162− prop-format='innerLabel'
163− type='text'
164− name='shard3'
165− placeholder={ `e.g. ${phTick}` }
166− value={ shard3 }
167− onChange={ inp => this.handleShardInput(SHARDS.SHARD3, inp) }>
168− <InnerLabel>{ 'Shard 3' }</InnerLabel>
156+ className="mono mt-8"
157+ prop-size="md"
158+ prop-format="innerLabel"
159+ type="text"
160+ name="shard3"
161+ placeholder={`e.g. ${phTick}`}
162+ value={shard3}
163+ onChange={inp => this.handleShardInput(SHARDS.SHARD3, inp)}>
164+ <InnerLabel>{'Shard 3'}</InnerLabel>
169165 </ShardInput>
170166
171167 <InputCaption>
172− { 'If your wallet requires a passphrase, you may enter it below.' }
168+ {'If your wallet requires a passphrase, you may enter it below.'}
173169 </InputCaption>
174170
175171 <Input
176− className='pt-8'
177− prop-size='md'
178− prop-format='innerLabel'
179− name='passphrase'
180− type='password'
181− value={ passphrase }
182− autocomplete='off'
183− onChange={ this.handlePassphraseInput }>
172+ className="pt-8"
173+ prop-size="md"
174+ prop-format="innerLabel"
175+ name="passphrase"
176+ type="password"
177+ value={passphrase}
178+ autocomplete="off"
179+ onChange={this.handlePassphraseInput}>
184180 <InnerLabel>{'Passphrase'}</InnerLabel>
185181 </Input>
186182
187183 <Button
188− disabled={ !ready }
184+ disabled={!ready}
189185 className={'mt-8'}
190186 prop-size={'lg wide'}
191187 // prop-color={this.buttonTriState(wallet)}
@@ -195,7 +191,8 @@ class Shards extends React.Component {
195191 shard2,
196192 shard3,
197193 pointName,
198− passphrase)
194+ passphrase
195+ )
199196 }>
200197 {'Unlock Wallet →'}
201198 </Button>
@@ -203,20 +200,17 @@ class Shards extends React.Component {
203200 <Button
204201 className={'mt-4'}
205202 prop-size={'xl wide'}
206− disabled={ Nothing.hasInstance(wallet) }
207− onClick={ () => {
208− popRoute()
209− pushRoute(ROUTE_NAMES.SHIPS)
210− }
211− }
212− >
213− { 'Continue →' }
203+ disabled={Nothing.hasInstance(wallet)}
204+ onClick={() => {
205+ popRoute();
206+ pushRoute(ROUTE_NAMES.SHIPS);
207+ }}>
208+ {'Continue →'}
214209 </Button>
215−
216210 </Col>
217211 </Row>
218− )
212+ );
219213 }
220214}
221215
222−export default Shards
216+export default Shards;
src/bridge/views/Ticket.js+72−79
@@ -1,126 +1,125 @@
1−import { Just, Nothing } from 'folktale/maybe'
2−import React from 'react'
3−import { Button } from '../components/Base'
1+import { Just, Nothing } from 'folktale/maybe';
2+import React from 'react';
3+import { Button } from '../components/Base';
44import {
55 InnerLabel,
66 ValidatedSigil,
77 PointInput,
88 TicketInput,
99 Input,
10− InputCaption
11− } from '../components/Base'
12−import { Row, Col, H1, P } from '../components/Base'
10+ InputCaption,
11+} from '../components/Base';
12+import { Row, Col, H1, P } from '../components/Base';
1313
14−import { randomPatq } from '../lib/lib'
15−import { ROUTE_NAMES } from '../lib/router'
16−import { urbitWalletFromTicket } from '../lib/wallet'
14+import { randomPatq } from '../lib/lib';
15+import { ROUTE_NAMES } from '../lib/router';
16+import { urbitWalletFromTicket } from '../lib/wallet';
1717
1818class Ticket extends React.Component {
19−
2019 constructor(props) {
21− super(props)
20+ super(props);
2221
2322 this.state = {
2423 ticket: '',
2524 pointName: '',
2625 passphrase: '',
27− isUnlocking: false
28− }
26+ isUnlocking: false,
27+ };
2928
30− this.pointPlaceholder = randomPatq(4)
31− this.ticketPlaceholder = randomPatq(8)
29+ this.pointPlaceholder = randomPatq(4);
30+ this.ticketPlaceholder = randomPatq(8);
3231
33− this.handleTicketInput = this.handleTicketInput.bind(this)
34− this.handlePassphraseInput = this.handlePassphraseInput.bind(this)
35− this.handlePointNameInput = this.handlePointNameInput.bind(this)
32+ this.handleTicketInput = this.handleTicketInput.bind(this);
33+ this.handlePassphraseInput = this.handlePassphraseInput.bind(this);
34+ this.handlePointNameInput = this.handlePointNameInput.bind(this);
3635 }
3736
3837 handleTicketInput(ticket) {
39− this.setState({ ticket })
38+ this.setState({ ticket });
4039 }
4140
4241 handlePassphraseInput(passphrase) {
43− this.setState({ passphrase })
42+ this.setState({ passphrase });
4443 }
4544
4645 handlePointNameInput(pointName) {
4746 if (pointName.length < 15) {
48− this.setState({ pointName })
47+ this.setState({ pointName });
4948 }
5049 }
5150
5251 async walletFromTicket(ticket, pointName, passphrase) {
5352 this.setState({
54− isUnlocking: true
53+ isUnlocking: true,
5554 });
5655
5756 const uhdw = await urbitWalletFromTicket(ticket, pointName, passphrase);
5857 this.props.setUrbitWallet(Just(uhdw));
5958
6059 this.setState({
61− isUnlocking: false
60+ isUnlocking: false,
6261 });
6362 }
6463
6564 render() {
66− const { popRoute, pushRoute, wallet } = this.props
67− const { ticket, pointName, passphrase } = this.state
65+ const { popRoute, pushRoute, wallet } = this.props;
66+ const { ticket, pointName, passphrase } = this.state;
6867
69− const phPoint = this.pointPlaceholder
70− const phTick = this.ticketPlaceholder
68+ const phPoint = this.pointPlaceholder;
69+ const phTick = this.ticketPlaceholder;
7170
7271 return (
73− <Row>
74− <Col>
75− <H1>{ 'Authenticate' }</H1>
72+ <Row>
73+ <Col>
74+ <H1>{'Authenticate'}</H1>
7675
77− <P>
78− { `Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.` }
79− </P>
76+ <P>
77+ {`Please enter your point and Urbit master ticket here. This information is written on your Urbit HD paper wallets.`}
78+ </P>
8079
8180 <PointInput
82− className='mono mt-8'
83− prop-size='lg'
84− prop-format='innerLabel'
85− type='text'
81+ className="mono mt-8"
82+ prop-size="lg"
83+ prop-format="innerLabel"
84+ type="text"
8685 autoFocus
87− placeholder={ `e.g. ${phPoint}` }
88− value={ pointName }
89− onChange={ this.handlePointNameInput }>
90− <InnerLabel>{ 'Point' }</InnerLabel>
86+ placeholder={`e.g. ${phPoint}`}
87+ value={pointName}
88+ onChange={this.handlePointNameInput}>
89+ <InnerLabel>{'Point'}</InnerLabel>
9190 <ValidatedSigil
9291 className={'tr-0 mt-05 mr-0 abs'}
9392 patp={pointName}
9493 size={68}
95− margin={8} />
96− </PointInput>
97−
94+ margin={8}
95+ />
96+ </PointInput>
9897
9998 <TicketInput
100− className='mono mt-8'
101− prop-size='lg'
102− prop-format='innerLabel'
103− type='password'
104− name='ticket'
105− placeholder={ `e.g. ${phTick}` }
106− value={ ticket }
107− onChange={ this.handleTicketInput }>
108− <InnerLabel>{ 'Ticket' }</InnerLabel>
99+ className="mono mt-8"
100+ prop-size="lg"
101+ prop-format="innerLabel"
102+ type="password"
103+ name="ticket"
104+ placeholder={`e.g. ${phTick}`}
105+ value={ticket}
106+ onChange={this.handleTicketInput}>
107+ <InnerLabel>{'Ticket'}</InnerLabel>
109108 </TicketInput>
110109
111110 <InputCaption>
112− { 'If your wallet requires a passphrase, you may enter it below.' }
111+ {'If your wallet requires a passphrase, you may enter it below.'}
113112 </InputCaption>
114113
115114 <Input
116− className='pt-8'
117− prop-size='md'
118− prop-format='innerLabel'
119− name='passphrase'
120− type='password'
121− value={ passphrase }
122− autocomplete='off'
123− onChange={ this.handlePassphraseInput }>
115+ className="pt-8"
116+ prop-size="md"
117+ prop-format="innerLabel"
118+ name="passphrase"
119+ type="password"
120+ value={passphrase}
121+ autocomplete="off"
122+ onChange={this.handlePassphraseInput}>
124123 <InnerLabel>{'Passphrase'}</InnerLabel>
125124 </Input>
126125
@@ -128,14 +127,11 @@ class Ticket extends React.Component {
128127 className={'mt-8'}
129128 prop-size={'lg wide'}
130129 disabled={this.state.isUnlocking || Just.hasInstance(wallet)}
131− onClick={
132− () => this.walletFromTicket(ticket, pointName, passphrase)
130+ onClick={() =>
131+ this.walletFromTicket(ticket, pointName, passphrase)
133132 }>
134−
135133 <span className="relative">
136− {this.state.isUnlocking &&
137− <span className="btn-spinner"></span>
138− }
134+ {this.state.isUnlocking && <span className="btn-spinner"></span>}
139135 {'Unlock Wallet →'}
140136 </span>
141137 </Button>
@@ -143,20 +139,17 @@ class Ticket extends React.Component {
143139 <Button
144140 className={'mt-4'}
145141 prop-size={'xl wide'}
146− disabled={ Nothing.hasInstance(wallet) }
147− onClick={ () => {
148− popRoute()
149− pushRoute(ROUTE_NAMES.SHIPS)
150− }
151− }
152− >
153− { 'Continue →' }
142+ disabled={Nothing.hasInstance(wallet)}
143+ onClick={() => {
144+ popRoute();
145+ pushRoute(ROUTE_NAMES.SHIPS);
146+ }}>
147+ {'Continue →'}
154148 </Button>
155−
156149 </Col>
157150 </Row>
158− )
151+ );
159152 }
160153}
161154
162−export default Ticket
155+export default Ticket;
src/bridge/views/Transfer.js+56−49
@@ -1,48 +1,54 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import * as azimuth from 'azimuth-js'
4−import * as ob from 'urbit-ob'
5−import * as need from '../lib/need'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import * as azimuth from 'azimuth-js';
4+import * as ob from 'urbit-ob';
5+import * as need from '../lib/need';
66
7−import { Row, Col, H1, InnerLabel, ShowBlockie, P, Anchor } from '../components/Base'
8−import { AddressInput } from '../components/Base'
9−import StatelessTransaction from '../components/StatelessTransaction'
7+import {
8+ Row,
9+ Col,
10+ H1,
11+ InnerLabel,
12+ ShowBlockie,
13+ P,
14+ Anchor,
15+} from '../components/Base';
16+import { AddressInput } from '../components/Base';
17+import StatelessTransaction from '../components/StatelessTransaction';
1018
11−import { NETWORK_NAMES } from '../lib/network'
19+import { NETWORK_NAMES } from '../lib/network';
1220
13−import {
14− isValidAddress
15−} from '../lib/wallet'
21+import { isValidAddress } from '../lib/wallet';
1622
1723class Transfer extends React.Component {
1824 constructor(props) {
19− super(props)
25+ super(props);
2026
2127 const issuingPoint = need.pointCursor(props);
2228
2329 this.state = {
2430 receivingAddress: '',
2531 issuingPoint: issuingPoint,
26− }
32+ };
2733
28− this.handleAddressInput = this.handleAddressInput.bind(this)
29− this.createUnsignedTxn = this.createUnsignedTxn.bind(this)
34+ this.handleAddressInput = this.handleAddressInput.bind(this);
35+ this.createUnsignedTxn = this.createUnsignedTxn.bind(this);
3036 this.statelessRef = React.createRef();
3137 }
3238
3339 handleAddressInput(receivingAddress) {
34− this.setState({ receivingAddress })
35− this.statelessRef.current.clearTxn()
40+ this.setState({ receivingAddress });
41+ this.statelessRef.current.clearTxn();
3642 }
3743
3844 handleConfirmAvailability() {
3945 this.confirmPointAvailability().then(r => {
40− this.setState({ isAvailable: r })
41− })
46+ this.setState({ isAvailable: r });
47+ });
4248 }
4349
4450 createUnsignedTxn() {
45− const { state, props } = this
51+ const { state, props } = this;
4652
4753 const validContracts = need.contracts(props);
4854
@@ -52,60 +58,60 @@ class Transfer extends React.Component {
5258 validContracts,
5359 validPoint,
5460 state.receivingAddress
55− )
61+ );
5662
57− return Maybe.Just(txn)
63+ return Maybe.Just(txn);
5864 }
5965
6066 render() {
61− const { props, state } = this
67+ const { props, state } = this;
6268
63− const validAddress = isValidAddress(state.receivingAddress)
64− const canGenerate = validAddress === true
69+ const validAddress = isValidAddress(state.receivingAddress);
70+ const canGenerate = validAddress === true;
6571
6672 const esvisible =
67− props.networkType === NETWORK_NAMES.ROPSTEN ||
68− props.networkType === NETWORK_NAMES.MAINNET
73+ props.networkType === NETWORK_NAMES.ROPSTEN ||
74+ props.networkType === NETWORK_NAMES.MAINNET;
6975
7076 const esdomain =
71− props.networkType === NETWORK_NAMES.ROPSTEN
72− ? "ropsten.etherscan.io"
73− : "etherscan.io"
77+ props.networkType === NETWORK_NAMES.ROPSTEN
78+ ? 'ropsten.etherscan.io'
79+ : 'etherscan.io';
7480
7581 return (
7682 <Row>
7783 <Col>
7884 <H1>
79− { 'Transfer'} <code>{ ` ${ob.patp(state.issuingPoint)} ` }</code>
80− { 'To a New Owner' }
85+ {'Transfer'} <code>{` ${ob.patp(state.issuingPoint)} `}</code>
86+ {'To a New Owner'}
8187 </H1>
8288
8389 <P>
84− {
85− `Please provide the Ethereum address of the new owner. You own these
90+ {`Please provide the Ethereum address of the new owner. You own these
8691 points until the recipient accepts the incoming transfer.
87− You may cancel the transfer until the transfer is accepted.`
88− }
92+ You may cancel the transfer until the transfer is accepted.`}
8993 </P>
9094
9195 <AddressInput
9296 className={'mono mt-8'}
93− prop-size='lg'
94− prop-format='innerLabel'
95− value={ state.receivingAddress }
96− placeholder={ `e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f` }
97− onChange={ v => this.handleAddressInput(v) }>
98− <InnerLabel>{ 'New ownership address' }</InnerLabel>
97+ prop-size="lg"
98+ prop-format="innerLabel"
99+ value={state.receivingAddress}
100+ placeholder={`e.g. 0x84295d5e054d8cff5a22428b195f5a1615bd644f`}
101+ onChange={v => this.handleAddressInput(v)}>
102+ <InnerLabel>{'New ownership address'}</InnerLabel>
99103 <ShowBlockie className={'mt-1'} address={state.receivingAddress} />
100104 </AddressInput>
101105
102106 <Anchor
103107 className={'mt-1'}
104108 prop-size={'s'}
105− prop-disabled={!isValidAddress(state.receivingAddress) || !esvisible}
109+ prop-disabled={
110+ !isValidAddress(state.receivingAddress) || !esvisible
111+ }
106112 target={'_blank'}
107113 href={`https://${esdomain}/address/${state.receivingAddress}`}>
108− {'View on Etherscan ↗'}
114+ {'View on Etherscan ↗'}
109115 </Anchor>
110116
111117 <StatelessTransaction
@@ -113,14 +119,15 @@ class Transfer extends React.Component {
113119 {...props}
114120 // Checks
115121 userApproval={state.userApproval}
116− canGenerate={ canGenerate }
122+ canGenerate={canGenerate}
117123 // Methods
118124 createUnsignedTxn={this.createUnsignedTxn}
119− ref={this.statelessRef} />
125+ ref={this.statelessRef}
126+ />
120127 </Col>
121128 </Row>
122− )
129+ );
123130 }
124131}
125132
126−export default Transfer
133+export default Transfer;
src/bridge/views/Trezor.js+89−95
@@ -1,28 +1,27 @@
1−import * as bip32 from 'bip32'
2−import React from 'react'
3−import Maybe from 'folktale/maybe'
4−import { Button } from '../components/Base'
5−import { Row, Col, H1, P } from '../components/Base'
6−import { InnerLabel, Input, InnerLabelDropdown } from '../components/Base'
7−import TrezorConnect from 'trezor-connect'
8−import * as secp256k1 from 'secp256k1'
9−
10−import { TREZOR_PATH } from '../lib/trezor'
11−import { ROUTE_NAMES } from '../lib/router'
1+import * as bip32 from 'bip32';
2+import React from 'react';
3+import Maybe from 'folktale/maybe';
4+import { Button } from '../components/Base';
5+import { Row, Col, H1, P } from '../components/Base';
6+import { InnerLabel, Input, InnerLabelDropdown } from '../components/Base';
7+import TrezorConnect from 'trezor-connect';
8+import * as secp256k1 from 'secp256k1';
9+
10+import { TREZOR_PATH } from '../lib/trezor';
11+import { ROUTE_NAMES } from '../lib/router';
1212
1313class Trezor extends React.Component {
14−
1514 constructor(props) {
16− super(props)
15+ super(props);
1716
1817 this.state = {
1918 hdpath: TREZOR_PATH.replace(/x/g, 0),
20− account: 0
21− }
19+ account: 0,
20+ };
2221
23− this.handleAccountSelection = this.handleAccountSelection.bind(this)
24− this.handleHdPathInput = this.handleHdPathInput.bind(this)
25− this.pollDevice = this.pollDevice.bind(this)
22+ this.handleAccountSelection = this.handleAccountSelection.bind(this);
23+ this.handleHdPathInput = this.handleHdPathInput.bind(this);
24+ this.pollDevice = this.pollDevice.bind(this);
2625 }
2726
2827 handleAccountSelection(account) {
@@ -34,116 +33,111 @@ class Trezor extends React.Component {
3433 }
3534
3635 handleHdPathInput(hdpath) {
37− this.setState({ hdpath })
36+ this.setState({ hdpath });
3837 }
3938
4039 async pollDevice() {
41− const { setWallet, setWalletHdPath } = this.props
42− const { hdpath } = this.state
40+ const { setWallet, setWalletHdPath } = this.props;
41+ const { hdpath } = this.state;
4342
4443 TrezorConnect.manifest({
4544 email: 'bridge-trezor@urbit.org',
46− appUrl: 'https://github.com/urbit/bridge'
45+ appUrl: 'https://github.com/urbit/bridge',
46+ });
47+ TrezorConnect.getPublicKey({ path: hdpath }).then(info => {
48+ if (info.success === true) {
49+ const payload = info.payload;
50+ const publicKey = Buffer.from(payload.publicKey, 'hex');
51+ const chainCode = Buffer.from(payload.chainCode, 'hex');
52+ const pub = secp256k1.publicKeyConvert(publicKey, true);
53+ const hd = bip32.fromPublicKey(pub, chainCode);
54+ setWallet(Maybe.Just(hd));
55+ setWalletHdPath(hdpath);
56+ } else {
57+ setWallet(Maybe.Nothing());
58+ }
4759 });
48− TrezorConnect.getPublicKey({ path: hdpath })
49− .then(info => {
50− if (info.success === true) {
51− const payload = info.payload
52− const publicKey = Buffer.from(payload.publicKey, 'hex')
53− const chainCode = Buffer.from(payload.chainCode, 'hex')
54− const pub = secp256k1.publicKeyConvert(publicKey, true)
55− const hd = bip32.fromPublicKey(pub, chainCode)
56− setWallet(Maybe.Just(hd))
57− setWalletHdPath(hdpath)
58− } else {
59− setWallet(Maybe.Nothing())
60− }
61− })
6260 }
6361
6462 render() {
65− const { pushRoute, popRoute, wallet } = this.props
66− const { hdpath, account } = this.state
63+ const { pushRoute, popRoute, wallet } = this.props;
64+ const { hdpath, account } = this.state;
6765 const { handleAccountSelection } = this;
6866
69− let accountOptions = [{
70− title: 'Custom path',
71− value: 'custom'
72− }];
67+ let accountOptions = [
68+ {
69+ title: 'Custom path',
70+ value: 'custom',
71+ },
72+ ];
7373 for (let i = 0; i < 20; i++) {
7474 accountOptions.push({
75− title: 'Account #' + (i+1),
76− value: i
75+ title: 'Account #' + (i + 1),
76+ value: i,
7777 });
7878 }
79− let accountTitle = accountOptions.find(o => (o.value === account)).title;
79+ let accountTitle = accountOptions.find(o => o.value === account).title;
8080
8181 const accountSelection = (
8282 <InnerLabelDropdown
83− className='mt-8'
84− prop-size='md'
85− prop-format='innerLabel'
83+ className="mt-8"
84+ prop-size="md"
85+ prop-format="innerLabel"
8686 options={accountOptions}
8787 handleUpdate={handleAccountSelection}
8888 title={'Account'}
8989 currentSelectionTitle={accountTitle}
90− fullWidth={true}
91− >
92− </InnerLabelDropdown>
90+ fullWidth={true}></InnerLabelDropdown>
9391 );
9492
95− const pathSelection = (account !== 'custom') ? null :
96− (
93+ const pathSelection =
94+ account !== 'custom' ? null : (
9795 <Input
98− className='mt-8 pt-8 text-mono'
99− prop-size='md'
100− prop-format='innerLabel'
101− name='hdpath'
102− value={ hdpath }
103− autocomplete='off'
104− onChange={ this.handleHdPathInput }>
96+ className="mt-8 pt-8 text-mono"
97+ prop-size="md"
98+ prop-format="innerLabel"
99+ name="hdpath"
100+ value={hdpath}
101+ autocomplete="off"
102+ onChange={this.handleHdPathInput}>
105103 <InnerLabel>{'HD Path'}</InnerLabel>
106104 </Input>
107105 );
108106
109107 return (
110− <Row>
111− <Col className={'measure-md'}>
112− <H1>{ 'Authenticate With Your Trezor' }</H1>
113−
114− <P>
115− { `Connect and authenticate to your Trezor. If you'd like
116− to use a custom derivation path, you may enter it below.`
117− }
118− </P>
119−
120− { accountSelection }
121− { pathSelection }
122−
123− <Button
124− className={'mt-8'}
125− prop-size={'wide lg'}
126− onClick={ this.pollDevice }>
127− { 'Authenticate →' }
128− </Button>
129−
130− <Button
131− className={'mt-8'}
132− prop-size={'wide lg'}
133− disabled={ Maybe.Nothing.hasInstance(wallet) }
134− onClick={
135− () => {
136− popRoute()
137− pushRoute(ROUTE_NAMES.SHIPS)
138− }
139− }>
140− { 'Continue →' }
141− </Button>
142−
108+ <Row>
109+ <Col className={'measure-md'}>
110+ <H1>{'Authenticate With Your Trezor'}</H1>
111+
112+ <P>
113+ {`Connect and authenticate to your Trezor. If you'd like
114+ to use a custom derivation path, you may enter it below.`}
115+ </P>
116+
117+ {accountSelection}
118+ {pathSelection}
119+
120+ <Button
121+ className={'mt-8'}
122+ prop-size={'wide lg'}
123+ onClick={this.pollDevice}>
124+ {'Authenticate →'}
125+ </Button>
126+
127+ <Button
128+ className={'mt-8'}
129+ prop-size={'wide lg'}
130+ disabled={Maybe.Nothing.hasInstance(wallet)}
131+ onClick={() => {
132+ popRoute();
133+ pushRoute(ROUTE_NAMES.SHIPS);
134+ }}>
135+ {'Continue →'}
136+ </Button>
143137 </Col>
144138 </Row>
145− )
139+ );
146140 }
147141}
148142
149−export default Trezor
143+export default Trezor;
src/bridge/views/ViewPoint.js+38−43
@@ -1,60 +1,58 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import * as ob from 'urbit-ob'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import * as ob from 'urbit-ob';
44
5−import { Button } from '../components/Base'
6−import { Row, Col, H1, P } from '../components/Base'
7−import { InnerLabel, PointInput, ValidatedSigil } from '../components/Base'
5+import { Button } from '../components/Base';
6+import { Row, Col, H1, P } from '../components/Base';
7+import { InnerLabel, PointInput, ValidatedSigil } from '../components/Base';
88
9−import { ROUTE_NAMES } from '../lib/router'
9+import { ROUTE_NAMES } from '../lib/router';
1010
1111class ViewPoint extends React.Component {
1212 constructor(props) {
13− super(props)
13+ super(props);
1414
1515 this.state = {
16− pointName: ''
17− }
16+ pointName: '',
17+ };
1818
19− this.handlePointInput = this.handlePointInput.bind(this)
19+ this.handlePointInput = this.handlePointInput.bind(this);
2020 }
2121
22− handlePointInput = (pointName) => {
22+ handlePointInput = pointName => {
2323 if (pointName.length < 15) {
24− this.setState({ pointName })
24+ this.setState({ pointName });
2525 }
26− }
26+ };
2727
2828 render() {
29− const { pointName } = this.state
30− const { popRoute, pushRoute, setPointCursor } = this.props
29+ const { pointName } = this.state;
30+ const { popRoute, pushRoute, setPointCursor } = this.props;
3131
3232 // NB (jtobin):
3333 //
3434 // could use a better patp parser in urbit-ob
35− let valid
35+ let valid;
3636 try {
37− valid = ob.isValidPatp(pointName)
38− } catch(err) {
39− valid = false
37+ valid = ob.isValidPatp(pointName);
38+ } catch (err) {
39+ valid = false;
4040 }
4141
4242 return (
4343 <Row>
4444 <Col>
45− <H1>{ 'View a Point' }</H1>
45+ <H1>{'View a Point'}</H1>
4646
47− <P>
48− { "Enter a point name to view its public information." }
49− </P>
47+ <P>{'Enter a point name to view its public information.'}</P>
5048
5149 <PointInput
5250 autoFocus
53− prop-size='lg'
54− prop-format='innerLabel'
55− className='mono'
56− placeholder='e.g. ~zod'
57− onChange={ this.handlePointInput }
51+ prop-size="lg"
52+ prop-format="innerLabel"
53+ className="mono"
54+ placeholder="e.g. ~zod"
55+ onChange={this.handlePointInput}
5856 value={pointName}>
5957 <InnerLabel>{'Point Name'}</InnerLabel>
6058 <ValidatedSigil
@@ -62,27 +60,24 @@ class ViewPoint extends React.Component {
6260 patp={pointName}
6361 show
6462 size={68}
65− margin={8} />
63+ margin={8}
64+ />
6665 </PointInput>
6766
6867 <Button
6968 className={'mt-8'}
70− disabled={ valid === false }
71− onClick={
72− () => {
73− setPointCursor(Maybe.Just(ob.patp2dec(pointName)))
74− popRoute()
75− pushRoute(ROUTE_NAMES.SHIP)
76− }
77− }
78− >
79− { 'Continue →' }
69+ disabled={valid === false}
70+ onClick={() => {
71+ setPointCursor(Maybe.Just(ob.patp2dec(pointName)));
72+ popRoute();
73+ pushRoute(ROUTE_NAMES.SHIP);
74+ }}>
75+ {'Continue →'}
8076 </Button>
8177 </Col>
8278 </Row>
83− )
79+ );
8480 }
85−
8681}
8782
88−export default ViewPoint
83+export default ViewPoint;
src/bridge/views/Wallet.js+76−70
@@ -1,101 +1,107 @@
1−import Maybe from 'folktale/maybe'
2−import React from 'react'
3−import { Button, H1, P } from '../components/Base'
4−import { InnerLabelDropdown } from '../components/Base'
5−import { Row, Col } from '../components/Base'
1+import Maybe from 'folktale/maybe';
2+import React from 'react';
3+import { Button, H1, P } from '../components/Base';
4+import { InnerLabelDropdown } from '../components/Base';
5+import { Row, Col } from '../components/Base';
66
7−import { ROUTE_NAMES } from '../lib/router'
8−import { WALLET_NAMES, renderWalletType } from '../lib/wallet'
7+import { ROUTE_NAMES } from '../lib/router';
8+import { WALLET_NAMES, renderWalletType } from '../lib/wallet';
99
1010class Wallet extends React.Component {
1111 componentDidMount() {
12− const { setWallet } = this.props
13− setWallet(Maybe.Nothing())
12+ const { setWallet } = this.props;
13+ setWallet(Maybe.Nothing());
1414 }
1515
1616 getWalletOptions() {
17− return [{
18− title: 'Urbit Master Ticket',
19− value: WALLET_NAMES.TICKET
20− }, {
21− title: 'Urbit Master Ticket (Shards)',
22− value: WALLET_NAMES.SHARDS
23− }, {
24− type: 'divider'
25− }, {
26− title: 'Ledger',
27− value: WALLET_NAMES.LEDGER
28− }, {
29− title: 'Trezor',
30− value: WALLET_NAMES.TREZOR
31− }, {
32− type: 'divider'
33− }, {
34− title: 'BIP39 Mnemonic',
35− value: WALLET_NAMES.MNEMONIC
36− }, {
37− title: 'Ethereum Private Key',
38− value: WALLET_NAMES.PRIVATE_KEY
39− }, {
40− title: 'Ethereum Keystore File',
41− value: WALLET_NAMES.KEYSTORE
42− }]
17+ return [
18+ {
19+ title: 'Urbit Master Ticket',
20+ value: WALLET_NAMES.TICKET,
21+ },
22+ {
23+ title: 'Urbit Master Ticket (Shards)',
24+ value: WALLET_NAMES.SHARDS,
25+ },
26+ {
27+ type: 'divider',
28+ },
29+ {
30+ title: 'Ledger',
31+ value: WALLET_NAMES.LEDGER,
32+ },
33+ {
34+ title: 'Trezor',
35+ value: WALLET_NAMES.TREZOR,
36+ },
37+ {
38+ type: 'divider',
39+ },
40+ {
41+ title: 'BIP39 Mnemonic',
42+ value: WALLET_NAMES.MNEMONIC,
43+ },
44+ {
45+ title: 'Ethereum Private Key',
46+ value: WALLET_NAMES.PRIVATE_KEY,
47+ },
48+ {
49+ title: 'Ethereum Keystore File',
50+ value: WALLET_NAMES.KEYSTORE,
51+ },
52+ ];
4353 }
4454
4555 render() {
46− const { props } = this
47− const walletOptions = this.getWalletOptions()
56+ const { props } = this;
57+ const walletOptions = this.getWalletOptions();
4858
4959 return (
5060 <Row>
5161 <Col>
52− <H1>{ 'Unlock a Wallet' }</H1>
62+ <H1>{'Unlock a Wallet'}</H1>
5363
54− <P>
55− {
56− `To manage your assets, you need to unlock a wallet.
57− Please select how you would like to access your wallet.`
58− }
59−
60− </P>
64+ <P>
65+ {`To manage your assets, you need to unlock a wallet.
66+ Please select how you would like to access your wallet.`}
67+ </P>
6168
6269 <InnerLabelDropdown
6370 className={'mb-10 mt-6'}
6471 title={'Wallet Type:'}
6572 options={walletOptions}
6673 handleUpdate={props.setWalletType}
67− currentSelectionTitle={renderWalletType(props.walletType)}>
68− </InnerLabelDropdown>
74+ currentSelectionTitle={renderWalletType(
75+ props.walletType
76+ )}></InnerLabelDropdown>
6977
7078 <Button
7179 className={'mt-10'}
72− onClick={
73− () => props.pushRoute(
74− props.walletType === WALLET_NAMES.MNEMONIC
75− ? ROUTE_NAMES.MNEMONIC
76− : props.walletType === WALLET_NAMES.TICKET
77− ? ROUTE_NAMES.TICKET
78− : props.walletType === WALLET_NAMES.SHARDS
79− ? ROUTE_NAMES.SHARDS
80− : props.walletType === WALLET_NAMES.LEDGER
81− ? ROUTE_NAMES.LEDGER
82− : props.walletType === WALLET_NAMES.TREZOR
83− ? ROUTE_NAMES.TREZOR
84− : props.walletType === WALLET_NAMES.PRIVATE_KEY
85− ? ROUTE_NAMES.PRIVATE_KEY
86− : props.walletType === WALLET_NAMES.KEYSTORE
87− ? ROUTE_NAMES.KEYSTORE
88− : ROUTE_NAMES.DEFAULT
80+ onClick={() =>
81+ props.pushRoute(
82+ props.walletType === WALLET_NAMES.MNEMONIC
83+ ? ROUTE_NAMES.MNEMONIC
84+ : props.walletType === WALLET_NAMES.TICKET
85+ ? ROUTE_NAMES.TICKET
86+ : props.walletType === WALLET_NAMES.SHARDS
87+ ? ROUTE_NAMES.SHARDS
88+ : props.walletType === WALLET_NAMES.LEDGER
89+ ? ROUTE_NAMES.LEDGER
90+ : props.walletType === WALLET_NAMES.TREZOR
91+ ? ROUTE_NAMES.TREZOR
92+ : props.walletType === WALLET_NAMES.PRIVATE_KEY
93+ ? ROUTE_NAMES.PRIVATE_KEY
94+ : props.walletType === WALLET_NAMES.KEYSTORE
95+ ? ROUTE_NAMES.KEYSTORE
96+ : ROUTE_NAMES.DEFAULT
8997 )
90− }
91− >
92− { 'Continue →' }
98+ }>
99+ {'Continue →'}
93100 </Button>
94101 </Col>
95102 </Row>
96− )
103+ );
97104 }
98−
99105}
100106
101−export default Wallet
107+export default Wallet;
src/common/components/AppNavigation.js+16−20
@@ -2,42 +2,38 @@ import React from 'react';
22
33class AppNavigation extends React.Component {
44 constructor(props) {
5− super(props)
5+ super(props);
66
7− this.navigate = this.navigate.bind(this)
7+ this.navigate = this.navigate.bind(this);
88 }
99
1010 navigate() {
1111 let appView;
12− if (this.props.appView === "bridge") appView = "walletgen"
13− if (this.props.appView === "walletgen") appView = "bridge"
12+ if (this.props.appView === 'bridge') appView = 'walletgen';
13+ if (this.props.appView === 'walletgen') appView = 'bridge';
1414
15− this.props.setView(appView)
15+ this.props.setView(appView);
1616 }
1717
1818 render() {
1919 let buttonText;
20− if (this.props.appView === "bridge") buttonText = "Go to Walletgen"
21− if (this.props.appView === "walletgen") buttonText = "Go to Bridge"
20+ if (this.props.appView === 'bridge') buttonText = 'Go to Walletgen';
21+ if (this.props.appView === 'walletgen') buttonText = 'Go to Bridge';
2222
2323 let style = {
24− position: "fixed",
25− bottom: "10px",
26− right: "10px",
27− textDecoration: "underline",
28− cursor: "pointer"
29− }
24+ position: 'fixed',
25+ bottom: '10px',
26+ right: '10px',
27+ textDecoration: 'underline',
28+ cursor: 'pointer',
29+ };
3030
3131 return (
32− <span
33− style={style}
34− onClick={this.navigate}>
32+ <span style={style} onClick={this.navigate}>
3533 {buttonText}
3634 </span>
37− )
35+ );
3836 }
3937}
4038
41−export {
42− AppNavigation
43−}
39+export { AppNavigation };
src/index.js+5−8
@@ -1,11 +1,8 @@
1−import 'babel-polyfill' // required for @ledgerhq/hw-transport-u2f
1+import 'babel-polyfill'; // required for @ledgerhq/hw-transport-u2f
22
3−import React from 'react'
4−import ReactDOM from 'react-dom'
3+import React from 'react';
4+import ReactDOM from 'react-dom';
55
6−import { Root } from './Root'
6+import { Root } from './Root';
77
8−ReactDOM.render(
9− <Root />,
10− document.getElementById('root')
11−)
8+ReactDOM.render(<Root />, document.getElementById('root'));
src/walletgen/Walletgen.js+64−86
@@ -1,68 +1,47 @@
1−import React from 'react'
2−import * as lodash from 'lodash'
1+import React from 'react';
2+import * as lodash from 'lodash';
33
4−import Header from './components/Header'
5−import Footer from './components/Footer'
4+import Header from './components/Header';
5+import Footer from './components/Footer';
66
7−import { NETWORK_STATES, PROFILE_STATES, GEN_STATES } from './lib/constants'
7+import { NETWORK_STATES, PROFILE_STATES, GEN_STATES } from './lib/constants';
88
9−import PrivacyPolicy from './views/PrivacyPolicy'
10−import Welcome from './views/Welcome'
11−import Eula from './views/Eula'
12−import EulaView from './views/EulaView'
13−import Upload from './views/Upload'
14−import Understand from './views/Understand'
15−import Generate from './views/Generate'
16−import Custody from './views/Custody'
17−import Download from './views/Download'
18−import Done from './views/Done'
9+import PrivacyPolicy from './views/PrivacyPolicy';
10+import Welcome from './views/Welcome';
11+import Eula from './views/Eula';
12+import EulaView from './views/EulaView';
13+import Upload from './views/Upload';
14+import Understand from './views/Understand';
15+import Generate from './views/Generate';
16+import Custody from './views/Custody';
17+import Download from './views/Download';
18+import Done from './views/Done';
1919
2020import './style/index.css';
2121
22−const checkNetwork = () => navigator.onLine
23− ? NETWORK_STATES.ONLINE
24− : NETWORK_STATES.OFFLINE
22+const checkNetwork = () =>
23+ navigator.onLine ? NETWORK_STATES.ONLINE : NETWORK_STATES.OFFLINE;
2524
2625const ROUTES = [
27− { path: '/Welcome',
28− Component: Welcome,
29− },
30− { path: '/Eula',
31− Component: Eula,
32− },
33− { path: '/Upload',
34− Component: Upload,
35− },
36− { path: '/Understand',
37− Component: Understand,
38− },
39− { path: '/Custody',
40− Component: Custody,
41− },
42− { path: '/Generate',
43− Component: Generate,
44− },
45− { path: '/Download',
46− Component: Download,
47− },
48− { path: '/Done',
49− Component: Done,
50− },
51− { path: '/PrivacyPolicy',
52− Component: PrivacyPolicy,
53− },
54− { path: '/EulaView',
55− Component: EulaView,
56− }
26+ { path: '/Welcome', Component: Welcome },
27+ { path: '/Eula', Component: Eula },
28+ { path: '/Upload', Component: Upload },
29+ { path: '/Understand', Component: Understand },
30+ { path: '/Custody', Component: Custody },
31+ { path: '/Generate', Component: Generate },
32+ { path: '/Download', Component: Download },
33+ { path: '/Done', Component: Done },
34+ { path: '/PrivacyPolicy', Component: PrivacyPolicy },
35+ { path: '/EulaView', Component: EulaView },
5736];
5837
5938const TOTAL_STEPS = 7;
6039
6140class Walletgen extends React.Component {
6241 constructor(props) {
63− super(props)
42+ super(props);
6443 this.state = {
65− 'network': checkNetwork(),
44+ network: checkNetwork(),
6645 'profile.state': PROFILE_STATES.NOSTART,
6746 'profile.value': null,
6847 'profile.shipCount': 0,
@@ -77,73 +56,72 @@ class Walletgen extends React.Component {
7756 'paperCollateral.state': GEN_STATES.PAPER_NOSTART,
7857 'paperCollateral.value': null,
7958 'paperCollateral.error': '',
80− 'route': '/Welcome',
81− 'lastRoute': '/Welcome',
82− 'currentStep': 1,
83− 'totalSteps': TOTAL_STEPS,
84− 'downloaded': false,
85− 'didClickDownload': false,
59+ route: '/Welcome',
60+ lastRoute: '/Welcome',
61+ currentStep: 1,
62+ totalSteps: TOTAL_STEPS,
63+ downloaded: false,
64+ didClickDownload: false,
8665 };
87− };
88−
66+ }
8967
9068 componentDidMount = () => {
91− window.addEventListener('online', () => this.setState({'network': NETWORK_STATES.ONLINE}));
92− window.addEventListener('offline', () => this.setState({'network': NETWORK_STATES.OFFLINE}));
69+ window.addEventListener('online', () =>
70+ this.setState({ network: NETWORK_STATES.ONLINE })
71+ );
72+ window.addEventListener('offline', () =>
73+ this.setState({ network: NETWORK_STATES.OFFLINE })
74+ );
9375 };
9476
95−
96− setGlobalState = state => this.setState(state)
77+ setGlobalState = state => this.setState(state);
9778
9879 router = (network, path) => {
9980 const route = key => {
100− const { Component } = lodash.find(ROUTES, { path: key })
81+ const { Component } = lodash.find(ROUTES, { path: key });
10182 const globals = {
10283 setGlobalState: this.setGlobalState,
103− ...this.state
104− }
105− return <Component { ...globals } />
106− }
84+ ...this.state,
85+ };
86+ return <Component {...globals} />;
87+ };
10788
108− return route(path)
89+ return route(path);
10990
11091 // return network === NETWORK_STATES.ONLINE
11192 // ? <NetworkWarning />
11293 // : !(network === NETWORK_STATES.OFFLINE)
11394 // ? <h1>Detecting your network state.</h1>
11495 // : route(path)
115− }
116−
96+ };
11797
11898 render() {
11999 const { network, route, currentStep, lastRoute } = this.state;
120100
121− const routed = this.router(network, route) // hacky prod
101+ const routed = this.router(network, route); // hacky prod
122102 // const routed = this.router(NETWORK_STATES.OFFLINE, true, route) // hacky dev
123103
124104 return (
125105 <main className={'container'}>
126−
127106 <Header
128− network={ network }
129− currentStep={ currentStep }
130− totalSteps={ TOTAL_STEPS } />
131−
132− <div className={ 'row wrapper' }>
133− <div className={'col-'}>
134− { routed }
135− </div>
107+ network={network}
108+ currentStep={currentStep}
109+ totalSteps={TOTAL_STEPS}
110+ />
111+
112+ <div className={'row wrapper'}>
113+ <div className={'col-'}>{routed}</div>
136114 <div className={'push'} />
137115 </div>
138116
139117 <Footer
140− route={ route }
141− lastRoute={ lastRoute }
142− setGlobalState={ this.setGlobalState }
118+ route={route}
119+ lastRoute={lastRoute}
120+ setGlobalState={this.setGlobalState}
143121 />
144122 </main>
145123 );
146− };
147−};
124+ }
125+}
148126
149−export default Walletgen
127+export default Walletgen;
src/walletgen/components/Button.js+5−9
@@ -1,13 +1,9 @@
1−import React from 'react'
1+import React from 'react';
22
3−const Button = ({ disabled, onClick, text, className }) =>
4− <button
5− className={className}
6− onClick={onClick}
7− disabled={disabled}
8− >
3+const Button = ({ disabled, onClick, text, className }) => (
4+ <button className={className} onClick={onClick} disabled={disabled}>
95 {text}
106 </button>
7+);
118
12−export default Button
13−
9+export default Button;
src/walletgen/components/CheckBox.js+22−12
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
22
33const CheckBox = ({
44 name,
@@ -9,7 +9,7 @@ const CheckBox = ({
99 className,
1010 state,
1111}) => {
12− const isCheckedClassname = state === true ? 'isChecked' : 'isNotChecked'
12+ const isCheckedClassname = state === true ? 'isChecked' : 'isNotChecked';
1313 return (
1414 <div className={`checkbox flex ${isCheckedClassname} ${className}`}>
1515 <label>
@@ -19,20 +19,30 @@ const CheckBox = ({
1919 type={'checkbox'}
2020 className={`rc-checkbox-input`}
2121 checked={state}
22− onChange={onChange} />
22+ onChange={onChange}
23+ />
2324
24− <svg width='20' height='20' viewBox='0 0 20 20' fill='none' xmlns='http://www.w3.org/2000/svg'>
25− <path d='M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z' transform='translate(3.84906 11.3371) rotate(-45)' fill='white'/>
25+ <svg
26+ width="20"
27+ height="20"
28+ viewBox="0 0 20 20"
29+ fill="none"
30+ xmlns="http://www.w3.org/2000/svg">
31+ <path
32+ d="M2 3.55661V0H0V5.55661H12.4182V3.55661H2Z"
33+ transform="translate(3.84906 11.3371) rotate(-45)"
34+ fill="white"
35+ />
2636 </svg>
27−
2837 </label>
2938 <div>
30− <p onChange={onChange} checked={state}>{title}</p>
31− { subtitle === '' ? null : <p>{subtitle}</p> }
39+ <p onChange={onChange} checked={state}>
40+ {title}
41+ </p>
42+ {subtitle === '' ? null : <p>{subtitle}</p>}
3243 </div>
3344 </div>
34− )
35−}
36−
45+ );
46+};
3747
38−export default CheckBox
48+export default CheckBox;
src/walletgen/components/EulaText.js+434−68
@@ -1,146 +1,512 @@
1−import React from 'react'
1+import React from 'react';
22
33// EULA
4−const EulaText = () =>
4+const EulaText = () => (
55 <span className={'legal'}>
66 <h2>Terms of Use</h2>
77
8− <p>This agreement (these <b>“Terms”</b>) governs the terms under which you may access and use the Wallet Generator software (the <b>“Software”</b>) made available to you by Tlon Corporation (“Tlon”, <b>“we”</b> or <b>“us”</b>). By clicking “I Accept,” or by downloading, installing, or otherwise accessing or using the Software, you agree that you have read and understood, and, as a condition to your use of the Software, you agree to be bound by, the following terms and conditions.
8+ <p>
9+ This agreement (these <b>“Terms”</b>) governs the terms under which you
10+ may access and use the Wallet Generator software (the <b>“Software”</b>)
11+ made available to you by Tlon Corporation (“Tlon”, <b>“we”</b> or{' '}
12+ <b>“us”</b>). By clicking “I Accept,” or by downloading, installing, or
13+ otherwise accessing or using the Software, you agree that you have read
14+ and understood, and, as a condition to your use of the Software, you agree
15+ to be bound by, the following terms and conditions.
916 </p>
1017
1118 <p>
12− <b>BY CLICKING “I ACCEPT,” OR BY DOWNLOADING, INSTALLING, OR OTHERWISE ACCESSING OR USING THE SOFTWARE</b>, YOU AGREE THAT YOU HAVE READ AND UNDERSTOOD, AND, AS A CONDITION TO YOUR USE OF THE SOFTWARE, YOU AGREE TO BE BOUND BY, THE FOLLOWING TERMS AND CONDITIONS (THESE “<b>TERMS</b>”). IF YOU ARE NOT ELIGIBLE, OR DO NOT AGREE TO THE TERMS, THEN YOU DO NOT HAVE OUR PERMISSION TO USE THE SOFTWARE. YOUR USE OF THE SOFTWARE, AND TLON’S PROVISION OF THE SOFTWARE TO YOU, CONSTITUTES AN AGREEMENT BY TLON AND BY YOU TO BE BOUND BY THESE TERMS.
19+ <b>
20+ BY CLICKING “I ACCEPT,” OR BY DOWNLOADING, INSTALLING, OR OTHERWISE
21+ ACCESSING OR USING THE SOFTWARE
22+ </b>
23+ , YOU AGREE THAT YOU HAVE READ AND UNDERSTOOD, AND, AS A CONDITION TO YOUR
24+ USE OF THE SOFTWARE, YOU AGREE TO BE BOUND BY, THE FOLLOWING TERMS AND
25+ CONDITIONS (THESE “<b>TERMS</b>”). IF YOU ARE NOT ELIGIBLE, OR DO NOT
26+ AGREE TO THE TERMS, THEN YOU DO NOT HAVE OUR PERMISSION TO USE THE
27+ SOFTWARE. YOUR USE OF THE SOFTWARE, AND TLON’S PROVISION OF THE SOFTWARE
28+ TO YOU, CONSTITUTES AN AGREEMENT BY TLON AND BY YOU TO BE BOUND BY THESE
29+ TERMS.
1330 </p>
1431
1532 <p>
16− <b><u>MATERIAL TERMS</u>: AS PROVIDED IN GREATER DETAIL IN THESE TERMS (AND WITHOUT LIMITING THE EXPRESS LANGUAGE OF THESE TERMS), YOU ACKNOWLEDGE THE FOLLOWING:</b>
33+ <b>
34+ <u>MATERIAL TERMS</u>: AS PROVIDED IN GREATER DETAIL IN THESE TERMS (AND
35+ WITHOUT LIMITING THE EXPRESS LANGUAGE OF THESE TERMS), YOU ACKNOWLEDGE
36+ THE FOLLOWING:
37+ </b>
1738 </p>
1839
1940 <ul>
20− <li><p><b>
21− WE DO NOT HOLD, BACK-UP OTHERWISE HAVE ANY ACCESS TO YOUR KEYS (DEFINED IN SECTION 1), SO YOU ARE SOLELY RESPONSIBLE FOR ANY LOSS OF THE SAME, AND YOU ARE SOLELY RESPONSIBLE FOR MAINTAINING ALL INFORMATION NECESSARY TO ACCESS YOUR KEYS AND WALLET;
22− </b></p></li>
23− <li><p><b>
24− WE HAVE NO LIABILITY WHATSOEVER FOR ANY SECURITY PROBLEMS OR INCIDENTS THAT YOU MAY EXPERIENCE IN CONNECTION WITH ANY USE OF THE SOFTWARE, INCLUDING ANY LOSS OR THEFT OF YOUR KEYS OR ANY PROBLEMS THAT MAY ARISE IN CONNECTION WITH YOUR WALLET;
25− </b></p></li>
26− <li><p><b>
27− THE SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND AND OUR LIABILITY TO YOU IN CONNECTION WITH THE SAME IS LIMITED; AND
28− </b></p></li>
29− <li><p><b>
30− ALL DISPUTES ARISING UNDER THESE TERMS WILL BE RESOLVED THROUGH BINDING ARBITRATION AND YOU AND TLON ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
31− </b></p></li>
41+ <li>
42+ <p>
43+ <b>
44+ WE DO NOT HOLD, BACK-UP OTHERWISE HAVE ANY ACCESS TO YOUR KEYS
45+ (DEFINED IN SECTION 1), SO YOU ARE SOLELY RESPONSIBLE FOR ANY LOSS
46+ OF THE SAME, AND YOU ARE SOLELY RESPONSIBLE FOR MAINTAINING ALL
47+ INFORMATION NECESSARY TO ACCESS YOUR KEYS AND WALLET;
48+ </b>
49+ </p>
50+ </li>
51+ <li>
52+ <p>
53+ <b>
54+ WE HAVE NO LIABILITY WHATSOEVER FOR ANY SECURITY PROBLEMS OR
55+ INCIDENTS THAT YOU MAY EXPERIENCE IN CONNECTION WITH ANY USE OF THE
56+ SOFTWARE, INCLUDING ANY LOSS OR THEFT OF YOUR KEYS OR ANY PROBLEMS
57+ THAT MAY ARISE IN CONNECTION WITH YOUR WALLET;
58+ </b>
59+ </p>
60+ </li>
61+ <li>
62+ <p>
63+ <b>
64+ THE SOFTWARE IS PROVIDED “AS IS” WITHOUT WARRANTIES OF ANY KIND AND
65+ OUR LIABILITY TO YOU IN CONNECTION WITH THE SAME IS LIMITED; AND
66+ </b>
67+ </p>
68+ </li>
69+ <li>
70+ <p>
71+ <b>
72+ ALL DISPUTES ARISING UNDER THESE TERMS WILL BE RESOLVED THROUGH
73+ BINDING ARBITRATION AND YOU AND TLON ARE EACH WAIVING THE RIGHT TO A
74+ TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
75+ </b>
76+ </p>
77+ </li>
3278 </ul>
3379
3480 <ol>
3581 <li>
36− <p><b>Software Overview.</b> The Software is an interface that you can use to generate a hierarchical deterministic wallet that enables the creation of child keys from parent keys in a hierarchy (“Wallet”). Those keys can be private keys or can involve a public Ethereum Wallet address that can be used to send and receive Urbit address space in transactions with third parties (such private keys and public addresses, the “Keys”). The Software may only be used if you are disconnected from the internet and will automatically close if it detects an internet connection. Tlon recommends that you use the Software only on an airgapped machine.</p>
82+ <p>
83+ <b>Software Overview.</b> The Software is an interface that you can
84+ use to generate a hierarchical deterministic wallet that enables the
85+ creation of child keys from parent keys in a hierarchy (“Wallet”).
86+ Those keys can be private keys or can involve a public Ethereum Wallet
87+ address that can be used to send and receive Urbit address space in
88+ transactions with third parties (such private keys and public
89+ addresses, the “Keys”). The Software may only be used if you are
90+ disconnected from the internet and will automatically close if it
91+ detects an internet connection. Tlon recommends that you use the
92+ Software only on an airgapped machine.
93+ </p>
3794 </li>
3895 <li>
39− <p><b>Eligibility.</b> You must be at least 18 years old to use the Software. By agreeing to these Terms, you represent and warrant to us that: (a) you are at least 18 years old; (b) your registration and your use of the Software is in compliance with any and all applicable laws and regulations; and (c) that all information you submit in connection with your download or use of the Software is truthful and accurate.
96+ <p>
97+ <b>Eligibility.</b> You must be at least 18 years old to use the
98+ Software. By agreeing to these Terms, you represent and warrant to us
99+ that: (a) you are at least 18 years old; (b) your registration and
100+ your use of the Software is in compliance with any and all applicable
101+ laws and regulations; and (c) that all information you submit in
102+ connection with your download or use of the Software is truthful and
103+ accurate.
40104 </p>
41105 </li>
42106 <li>
43− <p><b>License.</b></p>
107+ <p>
108+ <b>License.</b>
109+ </p>
44110 <ol>
45111 <li>
46− <p><b>Limited License.</b> Subject to your complete and ongoing compliance with these Terms, Tlon grants you, solely for your personal use, a limited, non-exclusive, non-transferable, non-sublicensable, revocable license to install and use one object code copy of the Software.
112+ <p>
113+ <b>Limited License.</b> Subject to your complete and ongoing
114+ compliance with these Terms, Tlon grants you, solely for your
115+ personal use, a limited, non-exclusive, non-transferable,
116+ non-sublicensable, revocable license to install and use one object
117+ code copy of the Software.
47118 </p>
48119 </li>
49120 <li>
50− <p><b>License Restrictions.</b> Except and solely to the extent such a restriction is impermissible under applicable law, you may not: (a) reproduce, distribute, publicly display, or publicly perform the Software; (b) make modifications to the Software; or (c) interfere with or circumvent any feature of the Software, including any security or access control mechanism. If you are prohibited under applicable law from using the Software, you may not use it.
121+ <p>
122+ <b>License Restrictions.</b> Except and solely to the extent such
123+ a restriction is impermissible under applicable law, you may not:
124+ (a) reproduce, distribute, publicly display, or publicly perform
125+ the Software; (b) make modifications to the Software; or (c)
126+ interfere with or circumvent any feature of the Software,
127+ including any security or access control mechanism. If you are
128+ prohibited under applicable law from using the Software, you may
129+ not use it.
51130 </p>
52131 </li>
53132 <li>
54− <p><b>Feedback.</b> If you choose to provide input and suggestions regarding problems with or proposed modifications or improvements to the Software (“<b>Feedback</b>”), then you hereby grant Tlon an unrestricted, perpetual, irrevocable, non-exclusive, fully-paid, royalty-free right to exploit the Feedback in any manner and for any purpose, including to improve the Software and create other products and services.
133+ <p>
134+ <b>Feedback.</b> If you choose to provide input and suggestions
135+ regarding problems with or proposed modifications or improvements
136+ to the Software (“<b>Feedback</b>”), then you hereby grant Tlon an
137+ unrestricted, perpetual, irrevocable, non-exclusive, fully-paid,
138+ royalty-free right to exploit the Feedback in any manner and for
139+ any purpose, including to improve the Software and create other
140+ products and services.
55141 </p>
56142 </li>
57143 </ol>
58144 </li>
59145
60146 <li>
61− <p><b>Ownership; Proprietary Rights.</b> The Software is owned and operated by Tlon. The visual interfaces, graphics, design, compilation, information, data, computer code (including source code or object code), products, software, services, and all other elements of the Software (“<b>Materials</b>”) provided by Tlon are protected by intellectual property and other laws. All Materials included in the Software are the property of Tlon or its third party licensors. Except as expressly authorized by Tlon, you may not make use of the Materials. Tlon reserves all rights to the Materials not granted expressly in these Terms.
147+ <p>
148+ <b>Ownership; Proprietary Rights.</b> The Software is owned and
149+ operated by Tlon. The visual interfaces, graphics, design,
150+ compilation, information, data, computer code (including source code
151+ or object code), products, software, services, and all other elements
152+ of the Software (“<b>Materials</b>”) provided by Tlon are protected by
153+ intellectual property and other laws. All Materials included in the
154+ Software are the property of Tlon or its third party licensors. Except
155+ as expressly authorized by Tlon, you may not make use of the
156+ Materials. Tlon reserves all rights to the Materials not granted
157+ expressly in these Terms.
62158 </p>
63159 </li>
64160
65161 <li>
66− <p><b>Third Party Software.</b> The Software may include or incorporate third party software components that are generally available free of charge under licenses granting recipients broad rights to copy, modify, and distribute those components (“<b>Third Party Components</b>”). Although the Software is provided to you subject to these Terms, nothing in these Terms prevents, restricts, or is intended to prevent or restrict you from obtaining Third Party Components under the applicable third party licenses or to limit your use of Third Party Components under those third party licenses.
162+ <p>
163+ <b>Third Party Software.</b> The Software may include or incorporate
164+ third party software components that are generally available free of
165+ charge under licenses granting recipients broad rights to copy,
166+ modify, and distribute those components (“
167+ <b>Third Party Components</b>”). Although the Software is provided to
168+ you subject to these Terms, nothing in these Terms prevents,
169+ restricts, or is intended to prevent or restrict you from obtaining
170+ Third Party Components under the applicable third party licenses or to
171+ limit your use of Third Party Components under those third party
172+ licenses.
67173 </p>
68174 </li>
69175
70176 <li>
71− <p><b>Prohibited Conduct.</b> BY USING THE SOFTWARE YOU AGREE NOT TO, AND AGREE NOT TO ATTEMPT TO OR PERMIT ANY OTHER PERSON TO:
177+ <p>
178+ <b>Prohibited Conduct.</b> BY USING THE SOFTWARE YOU AGREE NOT TO, AND
179+ AGREE NOT TO ATTEMPT TO OR PERMIT ANY OTHER PERSON TO:
72180 </p>
73181 <ol className={'ls-alpha'}>
74− <li><p>use the Software for any illegal purpose or in violation of any local, state, national, or international law;
75− </p></li>
76− <li><p>interfere with security-related features of the Software, including by: (i) disabling or circumventing features that prevent or limit use or copying of any content; or (ii) reverse engineering or otherwise attempting to discover the source code of any portion of the Software except to the extent that the activity is expressly permitted by applicable law, or except to the extent that Tlon has made such source code available to the public;
77− </p></li>
78− <li><p>perform any fraudulent activity including impersonating any person or entity, claiming a false affiliation, accessing any other Software account without permission; or
79− </p></li>
80− <li><p>sell or otherwise transfer the access granted under these Terms or any Materials (as defined in Section 4) or any right or ability to view, access, or use any Materials.
81− </p></li>
182+ <li>
183+ <p>
184+ use the Software for any illegal purpose or in violation of any
185+ local, state, national, or international law;
186+ </p>
187+ </li>
188+ <li>
189+ <p>
190+ interfere with security-related features of the Software,
191+ including by: (i) disabling or circumventing features that prevent
192+ or limit use or copying of any content; or (ii) reverse
193+ engineering or otherwise attempting to discover the source code of
194+ any portion of the Software except to the extent that the activity
195+ is expressly permitted by applicable law, or except to the extent
196+ that Tlon has made such source code available to the public;
197+ </p>
198+ </li>
199+ <li>
200+ <p>
201+ perform any fraudulent activity including impersonating any person
202+ or entity, claiming a false affiliation, accessing any other
203+ Software account without permission; or
204+ </p>
205+ </li>
206+ <li>
207+ <p>
208+ sell or otherwise transfer the access granted under these Terms or
209+ any Materials (as defined in Section 4) or any right or ability to
210+ view, access, or use any Materials.
211+ </p>
212+ </li>
82213 </ol>
83214 </li>
84215
85216 <li>
86− <p><b>Term, Termination and Modification of the Software.</b> These Terms are effective beginning when you accept the Terms or first download, install, access, or use the Software, and ending when terminated as described in this Section. If you violate any provision of these Terms, your authorization to access the Software and these Terms automatically terminate. In addition, Tlon may, at its sole discretion, terminate these Terms or suspend or terminate your right to access to the Software, at any time for any reason or no reason, with or without notice. Tlon reserves the right to modify or discontinue the Software at any time (including by limiting or discontinuing certain features of the Software), temporarily or permanently, without notice to you. Tlon will have no liability for any change to the Software or any suspension or termination of your access to or use of the Software. Upon any termination of these Terms, you must permanently delete all copies of the Software within your possession or control, and Sections 3.3, 4, 8, 9, 10, 11, and 12 will survive.
217+ <p>
218+ <b>Term, Termination and Modification of the Software.</b> These Terms
219+ are effective beginning when you accept the Terms or first download,
220+ install, access, or use the Software, and ending when terminated as
221+ described in this Section. If you violate any provision of these
222+ Terms, your authorization to access the Software and these Terms
223+ automatically terminate. In addition, Tlon may, at its sole
224+ discretion, terminate these Terms or suspend or terminate your right
225+ to access to the Software, at any time for any reason or no reason,
226+ with or without notice. Tlon reserves the right to modify or
227+ discontinue the Software at any time (including by limiting or
228+ discontinuing certain features of the Software), temporarily or
229+ permanently, without notice to you. Tlon will have no liability for
230+ any change to the Software or any suspension or termination of your
231+ access to or use of the Software. Upon any termination of these Terms,
232+ you must permanently delete all copies of the Software within your
233+ possession or control, and Sections 3.3, 4, 8, 9, 10, 11, and 12 will
234+ survive.
87235 </p>
88236 </li>
89237
90238 <li>
91− <p><b>Indemnity.</b> To the fullest extent permitted by law, you are responsible for your use of the Software, and you will defend and indemnify Tlon and its officers, directors, employees, consultants, affiliates, subsidiaries and agents (together, the “Tlon Entities”) from and against every claim brought by a third party, and any related liability, damage, loss, and expense, including reasonable attorneys’ fees and costs, arising out of or connected with: (a) your use of, or misuse of, the Software; and (b) your violation of any portion of these Terms, any representation, warranty, or any applicable law or regulation. We reserve the right, at our own expense, to assume the exclusive defense and control of any matter otherwise subject to indemnification by you (without limiting your indemnification obligations with respect to that matter), and in that case, you agree to cooperate with our defense of those claims.
239+ <p>
240+ <b>Indemnity.</b> To the fullest extent permitted by law, you are
241+ responsible for your use of the Software, and you will defend and
242+ indemnify Tlon and its officers, directors, employees, consultants,
243+ affiliates, subsidiaries and agents (together, the “Tlon Entities”)
244+ from and against every claim brought by a third party, and any related
245+ liability, damage, loss, and expense, including reasonable attorneys’
246+ fees and costs, arising out of or connected with: (a) your use of, or
247+ misuse of, the Software; and (b) your violation of any portion of
248+ these Terms, any representation, warranty, or any applicable law or
249+ regulation. We reserve the right, at our own expense, to assume the
250+ exclusive defense and control of any matter otherwise subject to
251+ indemnification by you (without limiting your indemnification
252+ obligations with respect to that matter), and in that case, you agree
253+ to cooperate with our defense of those claims.
92254 </p>
93255 </li>
94256
95257 <li>
96− <p><b>Disclaimers; No Warranties.</b> </p>
97− <p>THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE SOFTWARE ARE PROVIDED “AS IS” AND ON AN “AS AVAILABLE” BASIS. TLON DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED, RELATING TO THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE SOFTWARE, INCLUDING: (A) ANY IMPLIED WARRANTY OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, QUIET ENJOYMENT, OR NON-INFRINGEMENT; AND (B) ANY WARRANTY ARISING OUT OF COURSE OF DEALING, USAGE, OR TRADE. TLON DOES NOT WARRANT THAT THE SOFTWARE OR ANY PORTION OF THE SOFTWARE, OR ANY MATERIALS OR CONTENT OFFERED THROUGH THE SOFTWARE, WILL BE UNINTERRUPTED, SECURE, OR FREE OF ERRORS, VIRUSES, OR OTHER HARMFUL COMPONENTS, AND TLON DOES NOT WARRANT THAT ANY OF THOSE ISSUES WILL BE CORRECTED.
258+ <p>
259+ <b>Disclaimers; No Warranties.</b>{' '}
98260 </p>
99− <p>NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU FROM THE SOFTWARE OR TLON ENTITIES OR ANY MATERIALS OR CONTENT AVAILABLE THROUGH THE SOFTWARE WILL CREATE ANY WARRANTY REGARDING ANY OF THE TLON ENTITIES OR THE SOFTWARE THAT IS NOT EXPRESSLY STATED IN THESE TERMS. WE ARE NOT RESPONSIBLE FOR ANY DAMAGE THAT MAY RESULT FROM THE SOFTWARE.
261+ <p>
262+ THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE THROUGH THE
263+ SOFTWARE ARE PROVIDED “AS IS” AND ON AN “AS AVAILABLE” BASIS. TLON
264+ DISCLAIMS ALL WARRANTIES OF ANY KIND, WHETHER EXPRESS OR IMPLIED,
265+ RELATING TO THE SOFTWARE AND ALL MATERIALS AND CONTENT AVAILABLE
266+ THROUGH THE SOFTWARE, INCLUDING: (A) ANY IMPLIED WARRANTY OF
267+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, TITLE, QUIET
268+ ENJOYMENT, OR NON-INFRINGEMENT; AND (B) ANY WARRANTY ARISING OUT OF
269+ COURSE OF DEALING, USAGE, OR TRADE. TLON DOES NOT WARRANT THAT THE
270+ SOFTWARE OR ANY PORTION OF THE SOFTWARE, OR ANY MATERIALS OR CONTENT
271+ OFFERED THROUGH THE SOFTWARE, WILL BE UNINTERRUPTED, SECURE, OR FREE
272+ OF ERRORS, VIRUSES, OR OTHER HARMFUL COMPONENTS, AND TLON DOES NOT
273+ WARRANT THAT ANY OF THOSE ISSUES WILL BE CORRECTED.
100274 </p>
101− <p><b>YOU ARE SOLELY RESPONSIBLE FOR RECORDING AND SAFEGUARDING YOUR KEYS AND ANY INFORMATION THAT YOU MAY NEED TO RECOVER THE SAME. YOU UNDERSTAND AND AGREE THAT YOU USE ANY PORTION OF THE SOFTWARE AT YOUR OWN DISCRETION AND RISK, AND THAT WE ARE NOT RESPONSIBLE FOR ANY DAMAGE TO YOUR PROPERTY (INCLUDING YOUR COMPUTER SYSTEM OR MOBILE DEVICE USED IN CONNECTION WITH THE SOFTWARE) OR ANY LOSS OF DATA OR OTHER PROPERTY OR ASSETS YOU MAY OWN OR CONTROL IN CONNECTION WITH YOUR USE OF THE SOFTWARE, KEYS OR WALLET, INCLUDING ANY LOSS OF ACCESS TO YOUR WALLET OR KEYS.
102− </b></p>
103− <p>THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS IN THIS SECTION APPLY TO THE FULLEST EXTENT PERMITTED BY LAW. Tlon does not disclaim any warranty or other right that Tlon is prohibited from disclaiming under applicable law.
275+ <p>
276+ NO ADVICE OR INFORMATION, WHETHER ORAL OR WRITTEN, OBTAINED BY YOU
277+ FROM THE SOFTWARE OR TLON ENTITIES OR ANY MATERIALS OR CONTENT
278+ AVAILABLE THROUGH THE SOFTWARE WILL CREATE ANY WARRANTY REGARDING ANY
279+ OF THE TLON ENTITIES OR THE SOFTWARE THAT IS NOT EXPRESSLY STATED IN
280+ THESE TERMS. WE ARE NOT RESPONSIBLE FOR ANY DAMAGE THAT MAY RESULT
281+ FROM THE SOFTWARE.
282+ </p>
283+ <p>
284+ <b>
285+ YOU ARE SOLELY RESPONSIBLE FOR RECORDING AND SAFEGUARDING YOUR KEYS
286+ AND ANY INFORMATION THAT YOU MAY NEED TO RECOVER THE SAME. YOU
287+ UNDERSTAND AND AGREE THAT YOU USE ANY PORTION OF THE SOFTWARE AT
288+ YOUR OWN DISCRETION AND RISK, AND THAT WE ARE NOT RESPONSIBLE FOR
289+ ANY DAMAGE TO YOUR PROPERTY (INCLUDING YOUR COMPUTER SYSTEM OR
290+ MOBILE DEVICE USED IN CONNECTION WITH THE SOFTWARE) OR ANY LOSS OF
291+ DATA OR OTHER PROPERTY OR ASSETS YOU MAY OWN OR CONTROL IN
292+ CONNECTION WITH YOUR USE OF THE SOFTWARE, KEYS OR WALLET, INCLUDING
293+ ANY LOSS OF ACCESS TO YOUR WALLET OR KEYS.
294+ </b>
295+ </p>
296+ <p>
297+ THE LIMITATIONS, EXCLUSIONS AND DISCLAIMERS IN THIS SECTION APPLY TO
298+ THE FULLEST EXTENT PERMITTED BY LAW. Tlon does not disclaim any
299+ warranty or other right that Tlon is prohibited from disclaiming under
300+ applicable law.
104301 </p>
105302 </li>
106303
107304 <li>
108− <p><b>Limitation of Liability</b></p>
109− <p>TO THE FULLEST EXTENT PERMITTED BY LAW, IN NO EVENT WILL THE TLON ENTITIES BE LIABLE TO YOU FOR ANY INDIRECT, INCIDENTAL, SPECIAL, CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING DAMAGES FOR LOSS OF PROFITS, GOODWILL, OR ANY OTHER INTANGIBLE LOSS) ARISING OUT OF OR RELATING TO YOUR ACCESS TO OR USE OF, OR YOUR INABILITY TO ACCESS OR USE OR ANY OTHER PROBLEMS WITH, THE SOFTWARE, YOUR WALLET OR KEYS, OR ANY MATERIALS OR CONTENT WITHIN THE SOFTWARE, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), STATUTE, OR ANY OTHER LEGAL THEORY, AND WHETHER OR NOT ANY TLON ENTITY HAS BEEN INFORMED OF THE POSSIBILITY OF DAMAGE.
305+ <p>
306+ <b>Limitation of Liability</b>
307+ </p>
308+ <p>
309+ TO THE FULLEST EXTENT PERMITTED BY LAW, IN NO EVENT WILL THE TLON
310+ ENTITIES BE LIABLE TO YOU FOR ANY INDIRECT, INCIDENTAL, SPECIAL,
311+ CONSEQUENTIAL OR PUNITIVE DAMAGES (INCLUDING DAMAGES FOR LOSS OF
312+ PROFITS, GOODWILL, OR ANY OTHER INTANGIBLE LOSS) ARISING OUT OF OR
313+ RELATING TO YOUR ACCESS TO OR USE OF, OR YOUR INABILITY TO ACCESS OR
314+ USE OR ANY OTHER PROBLEMS WITH, THE SOFTWARE, YOUR WALLET OR KEYS, OR
315+ ANY MATERIALS OR CONTENT WITHIN THE SOFTWARE, WHETHER BASED ON
316+ WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), STATUTE, OR ANY OTHER
317+ LEGAL THEORY, AND WHETHER OR NOT ANY TLON ENTITY HAS BEEN INFORMED OF
318+ THE POSSIBILITY OF DAMAGE.
110319 </p>
111− <p>TO THE FULLEST EXTENT PERMITTED BY LAW AND EXCEPT AS OTHERWISE SET FORTH IN SECTION 11, THE AGGREGATE LIABILITY OF THE TLON ENTITIES TO YOU FOR ALL CLAIMS ARISING OUT OF OR RELATING TO THE USE OF OR ANY INABILITY TO USE ANY PORTION OF THE SOFTWARE OR OTHERWISE UNDER THESE TERMS, WHETHER IN CONTRACT, TORT, OR OTHERWISE, IS LIMITED TO $100.
320+ <p>
321+ TO THE FULLEST EXTENT PERMITTED BY LAW AND EXCEPT AS OTHERWISE SET
322+ FORTH IN SECTION 11, THE AGGREGATE LIABILITY OF THE TLON ENTITIES TO
323+ YOU FOR ALL CLAIMS ARISING OUT OF OR RELATING TO THE USE OF OR ANY
324+ INABILITY TO USE ANY PORTION OF THE SOFTWARE OR OTHERWISE UNDER THESE
325+ TERMS, WHETHER IN CONTRACT, TORT, OR OTHERWISE, IS LIMITED TO $100.
112326 </p>
113− <p>EACH PROVISION OF THESE TERMS THAT PROVIDES FOR A LIMITATION OF LIABILITY, DISCLAIMER OF WARRANTIES, OR EXCLUSION OF DAMAGES IS INTENDED TO AND DOES ALLOCATE THE RISKS BETWEEN THE PARTIES UNDER THESE TERMS. THIS ALLOCATION IS AN ESSENTIAL ELEMENT OF THE BASIS OF THE BARGAIN BETWEEN THE PARTIES. EACH OF THESE PROVISIONS IS SEVERABLE AND INDEPENDENT OF ALL OTHER PROVISIONS OF THESE TERMS. THE LIMITATIONS IN THIS SECTION 10 WILL APPLY EVEN IF ANY LIMITED REMEDY FAILS OF ITS ESSENTIAL PURPOSE.
327+ <p>
328+ EACH PROVISION OF THESE TERMS THAT PROVIDES FOR A LIMITATION OF
329+ LIABILITY, DISCLAIMER OF WARRANTIES, OR EXCLUSION OF DAMAGES IS
330+ INTENDED TO AND DOES ALLOCATE THE RISKS BETWEEN THE PARTIES UNDER
331+ THESE TERMS. THIS ALLOCATION IS AN ESSENTIAL ELEMENT OF THE BASIS OF
332+ THE BARGAIN BETWEEN THE PARTIES. EACH OF THESE PROVISIONS IS SEVERABLE
333+ AND INDEPENDENT OF ALL OTHER PROVISIONS OF THESE TERMS. THE
334+ LIMITATIONS IN THIS SECTION 10 WILL APPLY EVEN IF ANY LIMITED REMEDY
335+ FAILS OF ITS ESSENTIAL PURPOSE.
114336 </p>
115337 </li>
116338
117339 <li>
118− <p><b>Dispute Resolution and Arbitration</b></p>
340+ <p>
341+ <b>Dispute Resolution and Arbitration</b>
342+ </p>
119343 <ol>
120− <li><p><b>Generally.</b> In the interest of resolving disputes between you and Tlon in the most expedient and cost effective manner, and except as described in Section 11.2, you and Tlon agree that every dispute arising in connection with these Terms will be resolved by binding arbitration. Arbitration is less formal than a lawsuit in court. Arbitration uses a neutral arbitrator instead of a judge or jury, may allow for more limited discovery than in court, and can be subject to very limited review by courts. Arbitrators can award the same damages and relief that a court can award. This agreement to arbitrate disputes includes all claims arising out of or relating to any aspect of these Terms, whether based in contract, tort, statute, fraud, misrepresentation, or any other legal theory, and regardless of whether a claim arises during or after the termination of these Terms. YOU UNDERSTAND AND AGREE THAT, BY ENTERING INTO THESE TERMS, YOU AND Tlon ARE EACH WAIVING THE RIGHT TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
121− </p></li>
122− <li><p><b>Exceptions.</b> Despite the provisions of Section 11.1, nothing in these Terms will be deemed to waive, preclude, or otherwise limit the right of either party to: (a) bring an individual action in small claims court; (b) pursue an enforcement action through the applicable federal, state, or local agency if that action is available; (c) seek injunctive relief in a court of law in aid of arbitration; or (d) to file suit in a court of law to address an intellectual property infringement claim.
123− </p></li>
124− <li><p><b>Arbitrator.</b> Any arbitration between you and Tlon will be settled under the Federal Arbitration Act and administered by the American Arbitration Association (“AAA”) under its Consumer Arbitration Rules (collectively, “AAA Rules”) as modified by these Terms. The AAA Rules and filing forms are available online at www.adr.org, by calling the AAA at 1-800-778-7879, or by contacting Tlon. The arbitrator has exclusive authority to resolve any dispute relating to the interpretation, applicability, or enforceability of this binding arbitration agreement.
125− </p></li>
126− <li><p><b>Notice of Arbitration; Process.</b> A party who intends to seek arbitration must first send a written notice of the dispute to the other party by certified U.S. Mail or by Federal Express (signature required) or, only if that other party has not provided a current physical address, then by electronic mail (“<b>Notice of Arbitration</b>”). Tlon’s address for Notice is: Tlon Corporation, 584 Market St. #56196, San Francisco, CA 94104. The Notice of Arbitration must: (a) describe the nature and basis of the claim or dispute; and (b) set forth the specific relief sought (“<b>Demand</b>”). The parties will make good faith efforts to resolve the claim directly, but if the parties do not reach an agreement to do so within 30 days after the Notice of Arbitration is received, you or Tlon may commence an arbitration proceeding. All arbitration proceedings between the parties will be confidential unless otherwise agreed by the parties in writing. During the arbitration, the amount of any settlement offer made by you or Tlon must not be disclosed to the arbitrator until after the arbitrator makes a final decision and award, if any. If the arbitrator awards you an amount higher than the last written settlement amount offered by Tlon in settlement of the dispute prior to the award, Tlon will pay to you the higher of: (i) the amount awarded by the arbitrator; or (ii) $10,000.
127− </p></li>
128− <li><p><b>Fees.</b> If you commence arbitration in accordance with these Terms, Tlon will reimburse you for your payment of the filing fee, unless your claim is for more than $10,000, in which case the payment of any fees will be decided by the AAA Rules. Any arbitration hearing will take place at a location to be agreed upon in San Francisco County, California, but if the claim is for $10,000 or less, you may choose whether the arbitration will be conducted: (a) solely on the basis of documents submitted to the arbitrator; (b) through a non-appearance based telephone hearing; or (c) by an in-person hearing as established by the AAA Rules in the county (or parish) of your billing address. If the arbitrator finds that either the substance of your claim or the relief sought in the Demand is frivolous or brought for an improper purpose (as measured by the standards set forth in Federal Rule of Civil Procedure 11(b)), then the payment of all fees will be governed by the AAA Rules. In that case, you agree to reimburse Tlon for all monies previously disbursed by it that are otherwise your obligation to pay under the AAA Rules. Regardless of the manner in which the arbitration is conducted, the arbitrator must issue a reasoned written decision sufficient to explain the essential findings and conclusions on which the decision and award, if any, are based. The arbitrator may make rulings and resolve disputes as to the payment and reimbursement of fees or expenses at any time during the proceeding and upon request from either party made within 14 days of the arbitrator’s ruling on the merits.
129− </p></li>
130− <li><p><b>No Class Actions.</b> YOU AND TLON AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING. Further, unless both you and Tlon agree otherwise, the arbitrator may not consolidate more than one person’s claims, and may not otherwise preside over any form of a representative or class proceeding.
131− </p></li>
132− <li><p><b>Modifications to this Arbitration Provision.</b> If Tlon makes any future change to this arbitration provision, other than a change to Tlon’s address for Notice of Arbitration, you may reject the change by sending us written notice within 30 days of the change to Tlon’s address for Notice of Arbitration, in which case your account with Tlon will be immediately terminated and this arbitration provision, as in effect immediately prior to the changes you rejected will survive.
133− </p></li>
134− <li><p><b>Enforceability.</b> If Section 11.6 is found to be unenforceable or if the entirety of this Section 11 is found to be unenforceable, then the entirety of this Section 11 will be null and void and, in that case, the parties agree that the exclusive jurisdiction and venue described in Section 12 will govern any action arising out of or related to these Terms.
135− </p></li>
344+ <li>
345+ <p>
346+ <b>Generally.</b> In the interest of resolving disputes between
347+ you and Tlon in the most expedient and cost effective manner, and
348+ except as described in Section 11.2, you and Tlon agree that every
349+ dispute arising in connection with these Terms will be resolved by
350+ binding arbitration. Arbitration is less formal than a lawsuit in
351+ court. Arbitration uses a neutral arbitrator instead of a judge or
352+ jury, may allow for more limited discovery than in court, and can
353+ be subject to very limited review by courts. Arbitrators can award
354+ the same damages and relief that a court can award. This agreement
355+ to arbitrate disputes includes all claims arising out of or
356+ relating to any aspect of these Terms, whether based in contract,
357+ tort, statute, fraud, misrepresentation, or any other legal
358+ theory, and regardless of whether a claim arises during or after
359+ the termination of these Terms. YOU UNDERSTAND AND AGREE THAT, BY
360+ ENTERING INTO THESE TERMS, YOU AND Tlon ARE EACH WAIVING THE RIGHT
361+ TO A TRIAL BY JURY OR TO PARTICIPATE IN A CLASS ACTION.
362+ </p>
363+ </li>
364+ <li>
365+ <p>
366+ <b>Exceptions.</b> Despite the provisions of Section 11.1, nothing
367+ in these Terms will be deemed to waive, preclude, or otherwise
368+ limit the right of either party to: (a) bring an individual action
369+ in small claims court; (b) pursue an enforcement action through
370+ the applicable federal, state, or local agency if that action is
371+ available; (c) seek injunctive relief in a court of law in aid of
372+ arbitration; or (d) to file suit in a court of law to address an
373+ intellectual property infringement claim.
374+ </p>
375+ </li>
376+ <li>
377+ <p>
378+ <b>Arbitrator.</b> Any arbitration between you and Tlon will be
379+ settled under the Federal Arbitration Act and administered by the
380+ American Arbitration Association (“AAA”) under its Consumer
381+ Arbitration Rules (collectively, “AAA Rules”) as modified by these
382+ Terms. The AAA Rules and filing forms are available online at
383+ www.adr.org, by calling the AAA at 1-800-778-7879, or by
384+ contacting Tlon. The arbitrator has exclusive authority to resolve
385+ any dispute relating to the interpretation, applicability, or
386+ enforceability of this binding arbitration agreement.
387+ </p>
388+ </li>
389+ <li>
390+ <p>
391+ <b>Notice of Arbitration; Process.</b> A party who intends to seek
392+ arbitration must first send a written notice of the dispute to the
393+ other party by certified U.S. Mail or by Federal Express
394+ (signature required) or, only if that other party has not provided
395+ a current physical address, then by electronic mail (“
396+ <b>Notice of Arbitration</b>”). Tlon’s address for Notice is: Tlon
397+ Corporation, 584 Market St. #56196, San Francisco, CA 94104. The
398+ Notice of Arbitration must: (a) describe the nature and basis of
399+ the claim or dispute; and (b) set forth the specific relief sought
400+ (“<b>Demand</b>”). The parties will make good faith efforts to
401+ resolve the claim directly, but if the parties do not reach an
402+ agreement to do so within 30 days after the Notice of Arbitration
403+ is received, you or Tlon may commence an arbitration proceeding.
404+ All arbitration proceedings between the parties will be
405+ confidential unless otherwise agreed by the parties in writing.
406+ During the arbitration, the amount of any settlement offer made by
407+ you or Tlon must not be disclosed to the arbitrator until after
408+ the arbitrator makes a final decision and award, if any. If the
409+ arbitrator awards you an amount higher than the last written
410+ settlement amount offered by Tlon in settlement of the dispute
411+ prior to the award, Tlon will pay to you the higher of: (i) the
412+ amount awarded by the arbitrator; or (ii) $10,000.
413+ </p>
414+ </li>
415+ <li>
416+ <p>
417+ <b>Fees.</b> If you commence arbitration in accordance with these
418+ Terms, Tlon will reimburse you for your payment of the filing fee,
419+ unless your claim is for more than $10,000, in which case the
420+ payment of any fees will be decided by the AAA Rules. Any
421+ arbitration hearing will take place at a location to be agreed
422+ upon in San Francisco County, California, but if the claim is for
423+ $10,000 or less, you may choose whether the arbitration will be
424+ conducted: (a) solely on the basis of documents submitted to the
425+ arbitrator; (b) through a non-appearance based telephone hearing;
426+ or (c) by an in-person hearing as established by the AAA Rules in
427+ the county (or parish) of your billing address. If the arbitrator
428+ finds that either the substance of your claim or the relief sought
429+ in the Demand is frivolous or brought for an improper purpose (as
430+ measured by the standards set forth in Federal Rule of Civil
431+ Procedure 11(b)), then the payment of all fees will be governed by
432+ the AAA Rules. In that case, you agree to reimburse Tlon for all
433+ monies previously disbursed by it that are otherwise your
434+ obligation to pay under the AAA Rules. Regardless of the manner in
435+ which the arbitration is conducted, the arbitrator must issue a
436+ reasoned written decision sufficient to explain the essential
437+ findings and conclusions on which the decision and award, if any,
438+ are based. The arbitrator may make rulings and resolve disputes as
439+ to the payment and reimbursement of fees or expenses at any time
440+ during the proceeding and upon request from either party made
441+ within 14 days of the arbitrator’s ruling on the merits.
442+ </p>
443+ </li>
444+ <li>
445+ <p>
446+ <b>No Class Actions.</b> YOU AND TLON AGREE THAT EACH MAY BRING
447+ CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY
448+ AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR
449+ REPRESENTATIVE PROCEEDING. Further, unless both you and Tlon agree
450+ otherwise, the arbitrator may not consolidate more than one
451+ person’s claims, and may not otherwise preside over any form of a
452+ representative or class proceeding.
453+ </p>
454+ </li>
455+ <li>
456+ <p>
457+ <b>Modifications to this Arbitration Provision.</b> If Tlon makes
458+ any future change to this arbitration provision, other than a
459+ change to Tlon’s address for Notice of Arbitration, you may reject
460+ the change by sending us written notice within 30 days of the
461+ change to Tlon’s address for Notice of Arbitration, in which case
462+ your account with Tlon will be immediately terminated and this
463+ arbitration provision, as in effect immediately prior to the
464+ changes you rejected will survive.
465+ </p>
466+ </li>
467+ <li>
468+ <p>
469+ <b>Enforceability.</b> If Section 11.6 is found to be
470+ unenforceable or if the entirety of this Section 11 is found to be
471+ unenforceable, then the entirety of this Section 11 will be null
472+ and void and, in that case, the parties agree that the exclusive
473+ jurisdiction and venue described in Section 12 will govern any
474+ action arising out of or related to these Terms.
475+ </p>
476+ </li>
136477 </ol>
137478 </li>
138479
139− <li><p><b>Miscellaneous.</b> Your use of the Software is subject to all additional terms, policies, rules, or guidelines applicable to the Software or certain features of the Software that we may post on or link to from the Software (the “Additional Terms”). All Additional Terms are incorporated by this reference into, and made a part of, these Terms. You may not assign or transfer these Terms or your rights under these Terms, in whole or in part, by operation of law or otherwise, without our prior written consent. We may assign these Terms at any time without notice or consent. The failure to require performance of any provision will not affect our right to require performance at any other time after that, nor will a waiver by us of any breach or default of these Terms, or any provision of these Terms, be a waiver of any subsequent breach or default or a waiver of the provision itself. If any part of these Terms is held to be invalid or unenforceable, the unenforceable part will be given effect to the greatest extent possible, and the remaining parts will remain in full force and effect. These Terms are governed by the laws of the State of California without regard to conflict of law principles. You and Tlon submit to the personal and exclusive jurisdiction of the state courts and federal courts located within San Francisco County, California for resolution of any lawsuit or court proceeding permitted under these Terms. We are under no obligation to provide support for the Software. In instances where we may offer support, the support will be subject to published policies.
140− </p></li>
141−
480+ <li>
481+ <p>
482+ <b>Miscellaneous.</b> Your use of the Software is subject to all
483+ additional terms, policies, rules, or guidelines applicable to the
484+ Software or certain features of the Software that we may post on or
485+ link to from the Software (the “Additional Terms”). All Additional
486+ Terms are incorporated by this reference into, and made a part of,
487+ these Terms. You may not assign or transfer these Terms or your rights
488+ under these Terms, in whole or in part, by operation of law or
489+ otherwise, without our prior written consent. We may assign these
490+ Terms at any time without notice or consent. The failure to require
491+ performance of any provision will not affect our right to require
492+ performance at any other time after that, nor will a waiver by us of
493+ any breach or default of these Terms, or any provision of these Terms,
494+ be a waiver of any subsequent breach or default or a waiver of the
495+ provision itself. If any part of these Terms is held to be invalid or
496+ unenforceable, the unenforceable part will be given effect to the
497+ greatest extent possible, and the remaining parts will remain in full
498+ force and effect. These Terms are governed by the laws of the State of
499+ California without regard to conflict of law principles. You and Tlon
500+ submit to the personal and exclusive jurisdiction of the state courts
501+ and federal courts located within San Francisco County, California for
502+ resolution of any lawsuit or court proceeding permitted under these
503+ Terms. We are under no obligation to provide support for the Software.
504+ In instances where we may offer support, the support will be subject
505+ to published policies.
506+ </p>
507+ </li>
142508 </ol>
143−
144509 </span>
510+);
145511
146−export default EulaText
512+export default EulaText;
src/walletgen/components/Footer.js+24−28
@@ -1,46 +1,42 @@
1−import React from 'react'
1+import React from 'react';
22
3−import { version } from '../../../package.json'
3+import { version } from '../../../package.json';
44
55class Footer extends React.Component {
6−
76 render() {
87 // eslint-disable-next-line
9− const { setGlobalState, route } = this.props
8+ const { setGlobalState, route } = this.props;
109
1110 return (
1211 <footer className={'row h-13 mt-20'}>
12+ <div className={'col-md-3'}>{'Urbit Wallet Generator v' + version}</div>
13+
14+ <div className={'col-md-3'}>{'Tlon Corporation'}</div>
1315
1416 <div className={'col-md-3'}>
15− {'Urbit Wallet Generator v' + version }
17+ {/* eslint-disable-next-line */}
18+ <a
19+ href={'#'}
20+ onClick={() =>
21+ setGlobalState({ route: '/EulaView', lastRoute: route })
22+ }>
23+ EULA
24+ </a>
1625 </div>
1726
1827 <div className={'col-md-3'}>
19− {'Tlon Corporation'}
28+ {/* eslint-disable-next-line */}
29+ <a
30+ href={'#'}
31+ onClick={() =>
32+ setGlobalState({ route: '/PrivacyPolicy', lastRoute: route })
33+ }>
34+ Privacy Policy
35+ </a>
2036 </div>
21−
22− <div className={'col-md-3'}>
23− {/* eslint-disable-next-line */}
24− <a
25− href={'#'}
26− onClick={() => setGlobalState({ 'route': '/EulaView', 'lastRoute': route })}>
27− EULA
28− </a>
29− </div>
30−
31− <div className={'col-md-3'}>
32− {/* eslint-disable-next-line */}
33− <a
34− href={'#'}
35− onClick={() => setGlobalState({ 'route': '/PrivacyPolicy', 'lastRoute': route })}
36− >
37− Privacy Policy
38− </a>
39− </div>
40−
4137 </footer>
42− )
38+ );
4339 }
4440}
4541
46−export default Footer
42+export default Footer;
src/walletgen/components/Header.js+62−38
@@ -1,86 +1,110 @@
1−import React from 'react'
2−import { seq } from '../lib/lib'
1+import React from 'react';
2+import { seq } from '../lib/lib';
33
4−import { NETWORK_STATES } from '../lib/constants'
4+import { NETWORK_STATES } from '../lib/constants';
55
6−const StatusIndicator = props =>
6+const StatusIndicator = props => (
77 <div className={`flex h-9 align-center ${props.c2} flex-shrink-0`}>
88 <div className={`${props.c1} h-9`}>
9− {
10− props.error === true ? <Ex /> : <Check />
11− }
9+ {props.error === true ? <Ex /> : <Check />}
1210 </div>
13− <p className={'pr-4 text-sm text-600 c-white m-0'}>
14− { props.message }
15− </p>
11+ <p className={'pr-4 text-sm text-600 c-white m-0'}>{props.message}</p>
1612 </div>
13+);
1714
1815const ChooseIndicator = props =>
19− props.network === NETWORK_STATES.ONLINE
20− ? <StatusIndicator
16+ props.network === NETWORK_STATES.ONLINE ? (
17+ <StatusIndicator
2118 message={'You are online.'}
2219 c1={'bg-red'}
2320 c2={'bg-red'}
2421 error
2522 />
26− : props.network === NETWORK_STATES.OFFLINE
27− ? <StatusIndicator
23+ ) : props.network === NETWORK_STATES.OFFLINE ? (
24+ <StatusIndicator
2825 message={'You are offline.'}
2926 c1={'bg-green'}
3027 c2={'bg-green'}
3128 />
32− : <StatusIndicator
29+ ) : (
30+ <StatusIndicator
3331 message={'Checking network connection..'}
3432 c1={'bg-orange'}
3533 c2={'bg-orange'}
3634 />
35+ );
3736
38−const ProgressIndicator = ({ currentStep, totalSteps }) =>
37+const ProgressIndicator = ({ currentStep, totalSteps }) => (
3938 <div className={'relative h-9 col- mb-0'}>
40− <div className={'absolute ml-3 h-9 flex align-center text-sm white text-600'}>{`Step ${currentStep} of ${totalSteps}`}</div>
39+ <div
40+ className={
41+ 'absolute ml-3 h-9 flex align-center text-sm white text-600'
42+ }>{`Step ${currentStep} of ${totalSteps}`}</div>
4143 <div className={'flex'}>
42− {
43− seq(totalSteps).map((step, index) => <div key={index} className={`${ index+1 > currentStep ? 'bg-gray' : 'bg-blue' } h-9 col-`}>{''}</div> )
44− }
44+ {seq(totalSteps).map((step, index) => (
45+ <div
46+ key={index}
47+ className={`${
48+ index + 1 > currentStep ? 'bg-gray' : 'bg-blue'
49+ } h-9 col-`}>
50+ {''}
51+ </div>
52+ ))}
4553 </div>
46−
4754 </div>
55+);
4856
49−
50−const Header = props =>
57+const Header = props => (
5158 <header className={'col'}>
5259 <div className={'flex justify-between'}>
5360 <ProgressIndicator
5461 totalSteps={props.totalSteps}
5562 currentStep={props.currentStep}
5663 />
57− <ChooseIndicator
58− network={ props.network }
59− />
64+ <ChooseIndicator network={props.network} />
6065 </div>
6166 <div className={'col-md-8 h-15 flex align-center'}>
6267 <h1 className={'m-0 p-0'}>{'Wallet Generator'}</h1>
6368 </div>
6469 </header>
65−
66−
70+);
6771
6872const Ex = () => {
6973 return (
70− <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
74+ <svg
75+ width="36"
76+ height="36"
77+ viewBox="0 0 36 36"
78+ fill="none"
79+ xmlns="http://www.w3.org/2000/svg">
7180 <rect width="36" height="36" />
72− <path fillRule="evenodd" clipRule="evenodd" d="M16.2857 18L12 22.2857L13.7143 24L18 19.7143L22.2857 24L24 22.2857L19.7143 18L23.9999 13.7143L22.2857 12L18 16.2857L13.7143 12L12 13.7143L16.2857 18Z" fill="white"/>
81+ <path
82+ fillRule="evenodd"
83+ clipRule="evenodd"
84+ d="M16.2857 18L12 22.2857L13.7143 24L18 19.7143L22.2857 24L24 22.2857L19.7143 18L23.9999 13.7143L22.2857 12L18 16.2857L13.7143 12L12 13.7143L16.2857 18Z"
85+ fill="white"
86+ />
7387 </svg>
74− )
75−}
88+ );
89+};
7690
7791const Check = () => {
7892 return (
79− <svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
80− <rect width="36" height="36"/>
81− <path fillRule="evenodd" clipRule="evenodd" d="M24.9999 14.6329L23.2766 13L16.1699 19.7341L12.7233 16.4682L11 18.1012L16.1699 23L17.2451 21.9812L17.8932 21.3671L24.9999 14.6329Z" fill="white"/>
93+ <svg
94+ width="36"
95+ height="36"
96+ viewBox="0 0 36 36"
97+ fill="none"
98+ xmlns="http://www.w3.org/2000/svg">
99+ <rect width="36" height="36" />
100+ <path
101+ fillRule="evenodd"
102+ clipRule="evenodd"
103+ d="M24.9999 14.6329L23.2766 13L16.1699 19.7341L12.7233 16.4682L11 18.1012L16.1699 23L17.2451 21.9812L17.8932 21.3671L24.9999 14.6329Z"
104+ fill="white"
105+ />
82106 </svg>
83− )
84−}
107+ );
108+};
85109
86−export default Header
110+export default Header;
src/walletgen/components/InputBox.js+9−9
@@ -1,4 +1,4 @@
1−import React from 'react'
1+import React from 'react';
22
33const InputBox = ({
44 disabled,
@@ -9,7 +9,7 @@ const InputBox = ({
99 onChange,
1010 currentValue,
1111 placeholder,
12− containerClassnames
12+ containerClassnames,
1313}) => {
1414 return (
1515 <div className={containerClassnames}>
@@ -17,15 +17,15 @@ const InputBox = ({
1717 <input
1818 className={className}
1919 placeholder={placeholder}
20− type='text'
20+ type="text"
2121 value={currentValue}
2222 autoFocus={autofocus}
2323 autoComplete={autocomplete}
2424 disabled={disabled}
25− onChange={e => onChange(e)} />
26− </div> )
27−}
25+ onChange={e => onChange(e)}
26+ />
27+ </div>
28+ );
29+};
2830
29−
30−
31−export default InputBox
31+export default InputBox;
src/walletgen/components/ReactSVGComponents.js+37−21
@@ -1,63 +1,79 @@
1−import React from 'react'
2−import { get, map } from 'lodash'
1+import React from 'react';
2+import { get, map } from 'lodash';
33
44const ReactSVGComponents = {
55 svg: p => {
66 return (
77 <svg {...p.attr} version={'1.1'} xmlns={'http://www.w3.org/2000/svg'}>
8− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
8+ {map(get(p, 'children', []), child =>
9+ ReactSVGComponents[child.tag](child)
10+ )}
911 </svg>
10− )
12+ );
1113 },
1214 circle: p => {
1315 return (
1416 <circle {...p.attr}>
15− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
17+ {map(get(p, 'children', []), child =>
18+ ReactSVGComponents[child.tag](child)
19+ )}
1620 </circle>
17− )
21+ );
1822 },
1923 rect: p => {
2024 return (
2125 <rect {...p.attr}>
22− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
26+ {map(get(p, 'children', []), child =>
27+ ReactSVGComponents[child.tag](child)
28+ )}
2329 </rect>
24− )
30+ );
2531 },
2632 path: p => {
2733 return (
2834 <path {...p.attr}>
29− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
35+ {map(get(p, 'children', []), child =>
36+ ReactSVGComponents[child.tag](child)
37+ )}
3038 </path>
31− )
39+ );
3240 },
3341 g: p => {
3442 return (
3543 <g {...p.attr}>
36− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
44+ {map(get(p, 'children', []), child =>
45+ ReactSVGComponents[child.tag](child)
46+ )}
3747 </g>
38− )
48+ );
3949 },
4050 polygon: p => {
4151 return (
4252 <polygon {...p.attr}>
43− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
53+ {map(get(p, 'children', []), child =>
54+ ReactSVGComponents[child.tag](child)
55+ )}
4456 </polygon>
45− )
57+ );
4658 },
4759 line: p => {
4860 return (
4961 <line {...p.attr}>
50− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
62+ {map(get(p, 'children', []), child =>
63+ ReactSVGComponents[child.tag](child)
64+ )}
5165 </line>
52− )
66+ );
5367 },
5468 polyline: p => {
5569 return (
5670 <polyline {...p.attr}>
57− { map(get(p, 'children', []), child => ReactSVGComponents[child.tag](child)) }
71+ {map(get(p, 'children', []), child =>
72+ ReactSVGComponents[child.tag](child)
73+ )}
5874 </polyline>
59− )
60− }
61−}
75+ );
76+ },
77+};
6278
63−export default ReactSVGComponents
79+export default ReactSVGComponents;
src/walletgen/components/UploadButton.js+14−12
@@ -12,18 +12,20 @@ class UploadButton extends Component {
1212
1313 render() {
1414 return (
15− <label className={this.props.className}>
16− <input
17− className={'hidden'}
18− accept={'.txt'}
19− ref={(ref) => { this.input = ref; }}
20− onChange={this.onChange}
21− disabled={this.props.disabled}
22− type="file"
23− />
24− {this.props.children}
25− </label>
26− );
15+ <label className={this.props.className}>
16+ <input
17+ className={'hidden'}
18+ accept={'.txt'}
19+ ref={ref => {
20+ this.input = ref;
21+ }}
22+ onChange={this.onChange}
23+ disabled={this.props.disabled}
24+ type="file"
25+ />
26+ {this.props.children}
27+ </label>
28+ );
2729 }
2830}
2931
src/walletgen/lib/constants.js+16−16
@@ -1,21 +1,21 @@
1−const MIN_GALAXY = 0
2−const MAX_GALAXY = 255
3−const MIN_STAR = 256
4−const MAX_STAR = 65535
5−const MIN_PLANET = 65536
6−const MAX_PLANET = 4294967297
1+const MIN_GALAXY = 0;
2+const MAX_GALAXY = 255;
3+const MIN_STAR = 256;
4+const MAX_STAR = 65535;
5+const MIN_PLANET = 65536;
6+const MAX_PLANET = 4294967297;
77
8−const PLANET_ENTROPY_BITS = 64
9−const STAR_ENTROPY_BITS = 128
10−const GALAXY_ENTROPY_BITS = 384
8+const PLANET_ENTROPY_BITS = 64;
9+const STAR_ENTROPY_BITS = 128;
10+const GALAXY_ENTROPY_BITS = 384;
1111
12−const SEED_ENTROPY_BITS = 128
12+const SEED_ENTROPY_BITS = 128;
1313
1414const NETWORK_STATES = {
1515 ONLINE: Symbol('ONLINE'),
1616 OFFLINE: Symbol('OFFLINE'),
1717 UNKNOWN: Symbol('UNKNOWN'),
18−}
18+};
1919
2020const GEN_STATES = {
2121 ENY_NOSTART: Symbol('ENY_NOSTART'),
@@ -30,14 +30,14 @@ const GEN_STATES = {
3030 PAPER_PENDING: Symbol('PAPER_PENDING'),
3131 PAPER_SUCCESS: Symbol('PAPER_SUCCESS'),
3232 PAPER_FAILURE: Symbol('PAPER_FAILURE'),
33−}
33+};
3434
3535const BUTTON_STATES = {
3636 NOSTART: Symbol('NOSTART'),
3737 SUCCESS: Symbol('SUCCESS'),
3838 ERROR: Symbol('ERROR'),
3939 PENDING: Symbol('PENDING'),
40−}
40+};
4141
4242const PROFILE_STATES = {
4343 NOSTART: Symbol('NOSTART'),
@@ -45,7 +45,7 @@ const PROFILE_STATES = {
4545 UPLOAD_ERROR: Symbol('UPLOAD_ERROR'),
4646 INPUT_SUCCESS: Symbol('INPUT_SUCCESS'),
4747 INPUT_ERROR: Symbol('INPUT_ERROR'),
48−}
48+};
4949
5050export {
5151 NETWORK_STATES,
@@ -61,5 +61,5 @@ export {
6161 PLANET_ENTROPY_BITS,
6262 STAR_ENTROPY_BITS,
6363 GALAXY_ENTROPY_BITS,
64− SEED_ENTROPY_BITS
65−}
64+ SEED_ENTROPY_BITS,
65+};
src/walletgen/lib/lib.js+38−41
@@ -1,54 +1,56 @@
1−
2−import * as ob from 'urbit-ob'
3−import * as kg from '../../../node_modules/urbit-key-generation/dist/index'
4−import * as more from 'more-entropy'
5−import lodash from 'lodash'
1+import * as ob from 'urbit-ob';
2+import * as kg from '../../../node_modules/urbit-key-generation/dist/index';
3+import * as more from 'more-entropy';
4+import lodash from 'lodash';
65import {
7− MIN_STAR, MIN_PLANET, SEED_ENTROPY_BITS,
8− GALAXY_ENTROPY_BITS, STAR_ENTROPY_BITS, PLANET_ENTROPY_BITS,
9−} from './constants'
6+ MIN_STAR,
7+ MIN_PLANET,
8+ SEED_ENTROPY_BITS,
9+ GALAXY_ENTROPY_BITS,
10+ STAR_ENTROPY_BITS,
11+ PLANET_ENTROPY_BITS,
12+} from './constants';
1013
11−const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8
14+const SEED_LENGTH_BYTES = SEED_ENTROPY_BITS / 8;
1215
1316// returns a promise for a ticket string
1417const makeTicket = point => {
15−
16− const bits = point < MIN_STAR
17− ? GALAXY_ENTROPY_BITS
18− : point < MIN_PLANET
18+ const bits =
19+ point < MIN_STAR
20+ ? GALAXY_ENTROPY_BITS
21+ : point < MIN_PLANET
1922 ? STAR_ENTROPY_BITS
20− : PLANET_ENTROPY_BITS
23+ : PLANET_ENTROPY_BITS;
2124
22− const bytes = bits / 8
23− const some = new Uint8Array(bytes)
24− window.crypto.getRandomValues(some)
25+ const bytes = bits / 8;
26+ const some = new Uint8Array(bytes);
27+ window.crypto.getRandomValues(some);
2528
26− const gen = new more.Generator()
29+ const gen = new more.Generator();
2730
2831 return new Promise((resolve, reject) => {
2932 gen.generate(bits, result => {
30− const chunked = lodash.chunk(result, 2)
31− const desired = chunked.slice(0, bytes) // only take required entropy
32− const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1])
33− const entropy = lodash.zipWith(some, more, (x, y) => x ^ y)
34− const buf = Buffer.from(entropy)
35− const patq = ob.hex2patq(buf.toString('hex'))
36− resolve(patq)
37− reject('Entropy generation failed')
38− })
39− })
40−}
33+ const chunked = lodash.chunk(result, 2);
34+ const desired = chunked.slice(0, bytes); // only take required entropy
35+ const more = lodash.flatMap(desired, arr => arr[0] ^ arr[1]);
36+ const entropy = lodash.zipWith(some, more, (x, y) => x ^ y);
37+ const buf = Buffer.from(entropy);
38+ const patq = ob.hex2patq(buf.toString('hex'));
39+ resolve(patq);
40+ reject('Entropy generation failed');
41+ });
42+ });
43+};
4144
4245// return a wallet object
4346const generateWallet = async (point, ticket, boot) => {
44−
4547 const config = {
4648 ticket: ticket,
4749 seedSize: SEED_LENGTH_BYTES,
4850 ship: point,
4951 password: '',
5052 revisions: {},
51− boot: boot
53+ boot: boot,
5254 };
5355
5456 const wallet = await kg.generateWallet(config);
@@ -58,20 +60,15 @@ const generateWallet = async (point, ticket, boot) => {
5860 console.log('Generating Wallet for point address: ', point);
5961
6062 return wallet;
61−}
63+};
6264
6365// returns a promise
6466const generateOwnershipWallet = (ship, ticket) =>
6567 kg.generateOwnershipWallet({ ship, ticket });
6668
67−const seq = num => Array.from(Array(num), (_, i) => i)
69+const seq = num => Array.from(Array(num), (_, i) => i);
6870
69−const compose = (...fs) => fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v)
71+const compose = (...fs) =>
72+ fs.reduceRight((pF, nF) => (...args) => nF(pF(...args)), v => v);
7073
71−export {
72− makeTicket,
73− generateWallet,
74− generateOwnershipWallet,
75− compose,
76− seq,
77−}
74+export { makeTicket, generateWallet, generateOwnershipWallet, compose, seq };
src/walletgen/views/BrowserWarning.js+6−7
@@ -1,14 +1,13 @@
1−import React from 'react'
1+import React from 'react';
22
3−const BrowserWarning = (props) =>
3+const BrowserWarning = props => (
44 <div>
55 <h2>{'You must use this tool with Google Chrome.'}</h2>
66 <p>
7− {
8− `To continue, open this HTML file with Google Chrome. We don’t like the
9− new bubble UI either, but it's still the most secure browser.`
10− }
7+ {`To continue, open this HTML file with Google Chrome. We don’t like the
8+ new bubble UI either, but it's still the most secure browser.`}
119 </p>
1210 </div>
11+);
1312
14−export default BrowserWarning
13+export default BrowserWarning;
src/walletgen/views/Custody.js+9−8
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
22
3−import Button from '../components/Button'
3+import Button from '../components/Button';
44
55const NEXT_STEP_NUM = 5;
66
77class Custody extends React.Component {
88 render() {
9− let { setGlobalState } = this.props
9+ let { setGlobalState } = this.props;
1010
11− return(
11+ return (
1212 <div className={'col-md-6'}>
1313 <h2 className={'mb-4'}>{`A Word On Custody`}</h2>
1414
@@ -24,13 +24,14 @@ class Custody extends React.Component {
2424 <Button
2525 className={'btn btn-primary'}
2626 text={'I Understand →'}
27− onClick={ () => setGlobalState({ route: '/Generate', 'currentStep': NEXT_STEP_NUM })}
27+ onClick={() =>
28+ setGlobalState({ route: '/Generate', currentStep: NEXT_STEP_NUM })
29+ }
2830 />
2931 </div>
30−
3132 </div>
32− )
33+ );
3334 }
3435}
3536
36−export default Custody
37+export default Custody;
src/walletgen/views/Done.js+28−12
@@ -1,15 +1,16 @@
1−import React from 'react'
1+import React from 'react';
22
33class Done extends React.Component {
44 render() {
5− return(
5+ return (
66 <div className={'col-md-7'}>
7−
87 <h2>{`Next Steps`}</h2>
98
109 <ol>
1110 <li className={'mb-8'}>
12− <span className="h3">You <b>must</b> close this tab before connecting to the internet.</span>
11+ <span className="h3">
12+ You <b>must</b> close this tab before connecting to the internet.
13+ </span>
1314 </li>
1415
1516 <li className={'mb-8'}>
@@ -20,7 +21,11 @@ class Done extends React.Component {
2021 </li>
2122
2223 <li className={'mb-8'}>
23− <span className="h3">{'Upload your '}<code>{'urbit-registration.txt'}</code>{' to Registration'}</span>
24+ <span className="h3">
25+ {'Upload your '}
26+ <code>{'urbit-registration.txt'}</code>
27+ {' to Registration'}
28+ </span>
2429 <p className={'mt-0 ml-6'}>
2530 {`If you kept the Registration tab open, you can simply return there to upload. Otherwise, you can log into Registration again with the code in your email and navigate back to where you left off. If you do not upload this file to Registration, we will have no way to transfer your
2631 ships to you. This means you could lose your ships.`}
@@ -29,18 +34,29 @@ class Done extends React.Component {
2934
3035 <li className={'mb-8'}>
3136 <span className="h3">Print your paper wallets</span>
32− <p className={'ml-6 mv-0'}>You will lose your assets if you lose your wallets. Do not use a public printer. Please note that most modern printers contain immutable memory of all printed materials.</p>
37+ <p className={'ml-6 mv-0'}>
38+ You will lose your assets if you lose your wallets. Do not use a
39+ public printer. Please note that most modern printers contain
40+ immutable memory of all printed materials.
41+ </p>
3342 </li>
3443
3544 <li className={'mb-8'}>
36− <span className="h3">Securely erase the <span className={'text-mono'}>.zip</span> file and your wallet <span className={'text-mono'}>.png</span> files</span>
37− <p className={'ml-6 mv-0'}>If you used an airgapped machine, strongly consider destroying this machine.</p>
45+ <span className="h3">
46+ Securely erase the <span className={'text-mono'}>.zip</span> file
47+ and your wallet <span className={'text-mono'}>.png</span> files
48+ </span>
49+ <p className={'ml-6 mv-0'}>
50+ If you used an airgapped machine, strongly consider destroying
51+ this machine.
52+ </p>
3853 </li>
3954
4055 <li className={'mb-8'}>
41− <span className="h3">If you understand the above steps, you may now close this tab.</span>
56+ <span className="h3">
57+ If you understand the above steps, you may now close this tab.
58+ </span>
4259 </li>
43−
4460 </ol>
4561 {
4662 // <CheckBox
@@ -56,8 +72,8 @@ class Done extends React.Component {
5672 // />
5773 }
5874 </div>
59− )
75+ );
6076 }
6177}
6278
63−export default Done
79+export default Done;
src/walletgen/views/Download.js+105−84
@@ -1,40 +1,42 @@
1−import React from 'react'
2−import saveAs from 'file-saver'
3−import * as lodash from 'lodash'
1+import React from 'react';
2+import saveAs from 'file-saver';
3+import * as lodash from 'lodash';
44import JSZip from 'jszip';
55
6−import Button from '../components/Button'
7−import CheckBox from '../components/CheckBox'
6+import Button from '../components/Button';
7+import CheckBox from '../components/CheckBox';
88
99const NEXT_STEP_NUM = 7;
1010
1111class Download extends React.Component {
12− handleSave = (event) => {
13− const { props } = this
14−
15− const wallets = props['wallets.value']
16− const profile = props['profile.value']
17− const paper = props['paperCollateral.value']
18−
19− const urbit_registration = Object.entries(wallets).reduce((acc, [k, v]) => {
20− const owner = lodash.get(v, 'ownership.keys.address', '')
21− const transfer = ''
22− const spawn = lodash.get(v, 'spawn.keys.address', '')
23− const manage = lodash.get(v, 'management.keys.address', '')
24− const voting = lodash.get(v, 'voting.keys.address', '')
25− const auth = lodash.get(v, 'network.keys.auth.public', '')
26− const crypt = lodash.get(v, 'network.keys.crypt.public', '')
27− return {
28− ...acc,
29− [k]: [owner, transfer, spawn, manage, voting, auth, crypt],
30− }
31−
32− }, { idCode: profile.idCode });
12+ handleSave = event => {
13+ const { props } = this;
14+
15+ const wallets = props['wallets.value'];
16+ const profile = props['profile.value'];
17+ const paper = props['paperCollateral.value'];
18+
19+ const urbit_registration = Object.entries(wallets).reduce(
20+ (acc, [k, v]) => {
21+ const owner = lodash.get(v, 'ownership.keys.address', '');
22+ const transfer = '';
23+ const spawn = lodash.get(v, 'spawn.keys.address', '');
24+ const manage = lodash.get(v, 'management.keys.address', '');
25+ const voting = lodash.get(v, 'voting.keys.address', '');
26+ const auth = lodash.get(v, 'network.keys.auth.public', '');
27+ const crypt = lodash.get(v, 'network.keys.crypt.public', '');
28+ return {
29+ ...acc,
30+ [k]: [owner, transfer, spawn, manage, voting, auth, crypt],
31+ };
32+ },
33+ { idCode: profile.idCode }
34+ );
3335
3436 const json_urbit_registration = JSON.stringify(urbit_registration);
3537 const encoded_urbit_registration = new Blob(
3638 [btoa(json_urbit_registration)],
37− {type: "text/plain;charset=utf-8"}
39+ { type: 'text/plain;charset=utf-8' }
3840 );
3941
4042 const zip = new JSZip();
@@ -45,13 +47,13 @@ class Download extends React.Component {
4547 const bin3 = paper.filter(item => item.bin === '3');
4648 const bin4 = paper.filter(item => item.bin === '4');
4749
48− const bin0Folder = zip.folder("0. Public");
49− const bin1Folder = zip.folder("1. Very High Friction Custody");
50− const bin2Folder = zip.folder("2. High Friction Custody");
51− const bin3Folder = zip.folder("3. Medium Friction Custody");
52− const bin4Folder = zip.folder("4. Low Friction Custody");
50+ const bin0Folder = zip.folder('0. Public');
51+ const bin1Folder = zip.folder('1. Very High Friction Custody');
52+ const bin2Folder = zip.folder('2. High Friction Custody');
53+ const bin3Folder = zip.folder('3. Medium Friction Custody');
54+ const bin4Folder = zip.folder('4. Low Friction Custody');
5355
54− bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png))
56+ bin0.forEach(item => bin0Folder.file(`${item.pageTitle}.png`, item.png));
5557 bin1.forEach(item => bin1Folder.file(`${item.pageTitle}.png`, item.png));
5658 bin2.forEach(item => bin2Folder.file(`${item.pageTitle}.png`, item.png));
5759 bin3.forEach(item => bin3Folder.file(`${item.pageTitle}.png`, item.png));
@@ -61,87 +63,106 @@ class Download extends React.Component {
6163 // const jsonWallet = JSON.stringify(wallets, null, 2);
6264 // zip.file("jsonWallet.json", jsonWallet);
6365
64− zip.generateAsync({type:"blob"})
65− .then((content) => {
66− saveAs(content, 'urbit-wallets.zip')
66+ zip.generateAsync({ type: 'blob' }).then(content => {
67+ saveAs(content, 'urbit-wallets.zip');
6768 setTimeout(() => {
6869 saveAs(encoded_urbit_registration, 'urbit-registration.txt');
6970 props.setGlobalState({ didClickDownload: true });
7071 }, 2000);
7172 });
72−
73− }
73+ };
7474
7575 render() {
76+ const { setGlobalState } = this.props;
7677
77− const { setGlobalState } = this.props
78−
79− const { props } = this
78+ const { props } = this;
8079
81− const downloaded = props['downloaded']
82− const didClickDownload = props['didClickDownload']
80+ const downloaded = props['downloaded'];
81+ const didClickDownload = props['didClickDownload'];
8382
84− const nextButtonClass = downloaded ? 'btn btn-primary' : 'btn shape-gray-10'
83+ const nextButtonClass = downloaded
84+ ? 'btn btn-primary'
85+ : 'btn shape-gray-10';
8586
86− return(
87+ return (
8788 <div className={'col-md-6'}>
88− <h2 className={'mb-4'}>Your wallets have been generated. Download the files to a storage medium you trust.</h2>
89−
89+ <h2 className={'mb-4'}>
90+ Your wallets have been generated. Download the files to a storage
91+ medium you trust.
92+ </h2>
9093
9194 <div
92− className={ `white row text-mono pb-2` }
93− style={{borderBottom:'2px solid #222' }}
94− >
95+ className={`white row text-mono pb-2`}
96+ style={{ borderBottom: '2px solid #222' }}>
9597 <div className={'col-md-6 text-400'}>{'Filename'}</div>
9698 <div className={'col-md-6 text-400'}>{'Contents'}</div>
9799 </div>
98100
99−
100101 <div
101− className={ `white row pv-4` }
102− style={{borderBottom:'2px solid #222' }}
103− >
104− <div className={'col-md-6 text-mono text-600'}>{'urbit-wallets.zip'}</div>
105− <div className={'col-md-6'}><p className={'mv-0'}>Paper wallet PNG files for <code>{`${props['profile.shipCount']}`}</code> ships, organized by recommended custody tier. This contains your private keys, keep it safe. </p></div>
102+ className={`white row pv-4`}
103+ style={{ borderBottom: '2px solid #222' }}>
104+ <div className={'col-md-6 text-mono text-600'}>
105+ {'urbit-wallets.zip'}
106+ </div>
107+ <div className={'col-md-6'}>
108+ <p className={'mv-0'}>
109+ Paper wallet PNG files for{' '}
110+ <code>{`${props['profile.shipCount']}`}</code> ships, organized by
111+ recommended custody tier. This contains your private keys, keep it
112+ safe.{' '}
113+ </p>
114+ </div>
106115 </div>
107116
108−
109117 <div
110− className={ `white row pv-2` }
111− style={{borderBottom:'2px solid #222' }}
112− >
113− <div className={'col-md-6 text-600 text-mono'}>{'urbit-registration.txt'}</div>
114− <div className={'col-md-6 mv-0'}><p className={'mv-0'}>{`A file containing the public addresses of your wallets. You need to upload this to Registration next.`}</p></div>
118+ className={`white row pv-2`}
119+ style={{ borderBottom: '2px solid #222' }}>
120+ <div className={'col-md-6 text-600 text-mono'}>
121+ {'urbit-registration.txt'}
122+ </div>
123+ <div className={'col-md-6 mv-0'}>
124+ <p
125+ className={
126+ 'mv-0'
127+ }>{`A file containing the public addresses of your wallets. You need to upload this to Registration next.`}</p>
128+ </div>
115129 </div>
116130
117131 <Button
118132 className={'btn btn-primary mv-6'}
119133 text={'Download All'}
120− onClick={ e => this.handleSave(e) }
134+ onClick={e => this.handleSave(e)}
121135 />
122136
123− {
124− didClickDownload
125− ? <div>
126− <p>Please verify that the above files have been successfully downloaded. You may wish to unpack the zip file and inspect the contents. If you open your .png wallets on your screen, ensure you are in a secure location.</p>
127− <CheckBox
128− title={'I have my urbit-wallets.zip and urbit-registration.txt'}
129− onChange={ () => setGlobalState({ downloaded: !downloaded }) }
130− state={ downloaded }
131− />
132−
133− <Button
134− className={ nextButtonClass + ' mt-8' }
135− text={'Next →'}
136− disabled={ !downloaded }
137− onClick= { () => setGlobalState({ route: '/Done' , 'currentStep': NEXT_STEP_NUM })}
138− />
139− </div>
140− : <div />
141− }
137+ {didClickDownload ? (
138+ <div>
139+ <p>
140+ Please verify that the above files have been successfully
141+ downloaded. You may wish to unpack the zip file and inspect the
142+ contents. If you open your .png wallets on your screen, ensure you
143+ are in a secure location.
144+ </p>
145+ <CheckBox
146+ title={'I have my urbit-wallets.zip and urbit-registration.txt'}
147+ onChange={() => setGlobalState({ downloaded: !downloaded })}
148+ state={downloaded}
149+ />
150+
151+ <Button
152+ className={nextButtonClass + ' mt-8'}
153+ text={'Next →'}
154+ disabled={!downloaded}
155+ onClick={() =>
156+ setGlobalState({ route: '/Done', currentStep: NEXT_STEP_NUM })
157+ }
158+ />
159+ </div>
160+ ) : (
161+ <div />
162+ )}
142163 </div>
143− )
164+ );
144165 }
145166}
146167
147−export default Download
168+export default Download;
src/walletgen/views/Eula.js+21−22
@@ -1,10 +1,9 @@
1−import React from 'react'
1+import React from 'react';
22
3−import EulaText from '../components/EulaText'
4−import Button from '../components/Button'
5−
6−const NEXT_STEP_NUM = 2
3+import EulaText from '../components/EulaText';
4+import Button from '../components/Button';
75
6+const NEXT_STEP_NUM = 2;
87
98class Eula extends React.Component {
109 render() {
@@ -12,38 +11,38 @@ class Eula extends React.Component {
1211
1312 return (
1413 <div>
15−
1614 <EulaText />
1715
1816 <div className={'mt-8'}>
19−
2017 <Button
2118 className={'btn btn-primary mb-4'}
2219 text={'I Accept'}
23− onClick={ () => setGlobalState({
24− 'route': '/Upload',
25− 'currentStep': NEXT_STEP_NUM,
26− })}
20+ onClick={() =>
21+ setGlobalState({
22+ route: '/Upload',
23+ currentStep: NEXT_STEP_NUM,
24+ })
25+ }
2726 />
2827
2928 <Button
3029 className={'btn shape-gray-10 ml-2'}
3130 text={'I Do Not Accept'}
32− onClick={ () => setGlobalState({ 'route': '/Welcome' })}
31+ onClick={() => setGlobalState({ route: '/Welcome' })}
3332 />
34−
3533 </div>
3634
37− <p><strong>
38− By clicking “I Accept,” or by downloading, installing, or otherwise
39− accessing or using the Software, you agree that you have read and
40− understood, and, as a condition to your use of the Software, you agree
41− to be bound by, the above terms and conditions.
42− </strong></p>
43−
35+ <p>
36+ <strong>
37+ By clicking “I Accept,” or by downloading, installing, or otherwise
38+ accessing or using the Software, you agree that you have read and
39+ understood, and, as a condition to your use of the Software, you
40+ agree to be bound by, the above terms and conditions.
41+ </strong>
42+ </p>
4443 </div>
45− )
44+ );
4645 }
4746}
4847
49−export default Eula
48+export default Eula;
src/walletgen/views/EulaView.js+14−16
@@ -1,7 +1,7 @@
1−import React from 'react'
1+import React from 'react';
22
3−import EulaText from '../components/EulaText'
4−import Button from '../components/Button'
3+import EulaText from '../components/EulaText';
4+import Button from '../components/Button';
55
66class EulaView extends React.Component {
77 render() {
@@ -9,29 +9,27 @@ class EulaView extends React.Component {
99
1010 return (
1111 <div>
12−
1312 <EulaText />
1413
1514 <div className={'mt-8'}>
16−
1715 <Button
1816 className={'btn shape-gray-10 ml-2'}
1917 text={'Close'}
20− onClick={ () => setGlobalState({ 'route': lastRoute })}
18+ onClick={() => setGlobalState({ route: lastRoute })}
2119 />
22−
2320 </div>
2421
25− <p><strong>
26− By clicking “I Accept,” or by downloading, installing, or otherwise
27− accessing or using the Software, you agree that you have read and
28− understood, and, as a condition to your use of the Software, you agree
29− to be bound by, the above terms and conditions.
30− </strong></p>
31−
22+ <p>
23+ <strong>
24+ By clicking “I Accept,” or by downloading, installing, or otherwise
25+ accessing or using the Software, you agree that you have read and
26+ understood, and, as a condition to your use of the Software, you
27+ agree to be bound by, the above terms and conditions.
28+ </strong>
29+ </p>
3230 </div>
33− )
31+ );
3432 }
3533}
3634
37−export default EulaView
35+export default EulaView;
src/walletgen/views/Generate.js+221−178
@@ -1,46 +1,46 @@
1−import React from 'react'
2−import * as sigil from 'sigil-js'
3−import ob from 'urbit-ob'
4−import classnames from 'classnames'
5−import * as wg from '../lib/lib'
1+import React from 'react';
2+import * as sigil from 'sigil-js';
3+import ob from 'urbit-ob';
4+import classnames from 'classnames';
5+import * as wg from '../lib/lib';
66
7+import Button from '../components/Button';
8+import ReactSVGComponents from '../components/ReactSVGComponents';
9+import PaperCollateralRenderer from 'PaperCollateralRenderer';
710
8−import Button from '../components/Button'
9−import ReactSVGComponents from '../components/ReactSVGComponents'
10−import PaperCollateralRenderer from 'PaperCollateralRenderer'
11−
12−import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants'
11+import { MAX_GALAXY, MIN_STAR, MAX_STAR, GEN_STATES } from '../lib/constants';
1312
1413const NEXT_STEP_NUM = 6;
1514
1615const shipInfo = ship => {
17− const name = ob.patp(ship)
16+ const name = ob.patp(ship);
1817 const sig = sigil.pour({
1918 colorway: ['black', 'white'],
2019 patp: name,
2120 renderer: ReactSVGComponents,
22− size: 233
23− })
21+ size: 233,
22+ });
2423
2524 return ship < MAX_GALAXY
2625 ? galaxyInfo(name, sig)
2726 : ship < MAX_STAR
2827 ? starInfo(name, sig)
29− : planetInfo(name, sig)
30−}
28+ : planetInfo(name, sig);
29+};
3130
32−const infoHeader = ship =>
33− <div className={'row text-600 text-mono'} key={ ship + '-header' }>
31+const infoHeader = ship => (
32+ <div className={'row text-600 text-mono'} key={ship + '-header'}>
3433 {ship + ' Wallet'}
3534 </div>
35+);
3636
3737const infoSigil = (ship, sig) => {
3838 return (
39− <div className={ '' } key={ 'sigil-' + ship }>
40− { sig }
39+ <div className={''} key={'sigil-' + ship}>
40+ {sig}
4141 </div>
42− )
43−}
42+ );
43+};
4444
4545const getColor = (classOf, seedType) => {
4646 if (classOf === 'galaxy') {
@@ -59,110 +59,142 @@ const getColor = (classOf, seedType) => {
5959 if (seedType === 'manage') return 'bg-low';
6060 if (seedType === 'master') return 'bg-medium';
6161 }
62−
63−}
64−
65−
62+};
6663
6764const infoManagementSeed = ship => {
68− const classOf = ob.clan(ship)
69− const colorClass = getColor(classOf, 'manage')
70− return <Tile colorClass={colorClass} key={`info-${ship}-manage`} col1={`Management Proxy`} col2={''} />
71−
72−}
65+ const classOf = ob.clan(ship);
66+ const colorClass = getColor(classOf, 'manage');
67+ return (
68+ <Tile
69+ colorClass={colorClass}
70+ key={`info-${ship}-manage`}
71+ col1={`Management Proxy`}
72+ col2={''}
73+ />
74+ );
75+};
7376
7477const infoVotingSeed = ship => {
75− const classOf = ob.clan(ship)
76− const colorClass = getColor(classOf, 'voting')
77− return <Tile colorClass={colorClass} key={`info-${ship}-voting`} col1={`Voting Proxy`} col2={''} />
78−}
79−
80−
78+ const classOf = ob.clan(ship);
79+ const colorClass = getColor(classOf, 'voting');
80+ return (
81+ <Tile
82+ colorClass={colorClass}
83+ key={`info-${ship}-voting`}
84+ col1={`Voting Proxy`}
85+ col2={''}
86+ />
87+ );
88+};
8189
8290const infoSpawnSeed = ship => {
83− const classOf = ob.clan(ship)
84− const colorClass = getColor(classOf, 'spawn')
85− return <Tile colorClass={colorClass} key={`info-${ship}-spawn`} col1={`Spawn Proxy`} col2={''} />
86−}
87−
88−
91+ const classOf = ob.clan(ship);
92+ const colorClass = getColor(classOf, 'spawn');
93+ return (
94+ <Tile
95+ colorClass={colorClass}
96+ key={`info-${ship}-spawn`}
97+ col1={`Spawn Proxy`}
98+ col2={''}
99+ />
100+ );
101+};
89102
90103const infoMasterTicket = ship => {
91104 const classOf = ob.clan(ship);
92105 const colorClass = getColor(classOf, 'master');
93− return <Tile colorClass={colorClass} key={`info-${ship}-master`} col1={`Master Ticket`} col2={''} />;
106+ return (
107+ <Tile
108+ colorClass={colorClass}
109+ key={`info-${ship}-master`}
110+ col1={`Master Ticket`}
111+ col2={''}
112+ />
113+ );
94114};
95115
96−
97−
98116const infoMasterTicketShard = ship => {
99117 const classOf = ob.clan(ship);
100118 const colorClass = getColor(classOf, 'master');
101119 return [
102− <Tile colorClass={colorClass} key={`info-${ship}-master-1`} col1={`Master Ticket Shard 1 of 3`} col2={''} />,
103− <Tile colorClass={colorClass} key={`info-${ship}-master-2`} col1={`Master Ticket Shard 2 of 3`} col2={''} />,
104− <Tile colorClass={colorClass} key={`info-${ship}-master-3`} col1={`Master Ticket Shard 3 of 3`} col2={''} />,
120+ <Tile
121+ colorClass={colorClass}
122+ key={`info-${ship}-master-1`}
123+ col1={`Master Ticket Shard 1 of 3`}
124+ col2={''}
125+ />,
126+ <Tile
127+ colorClass={colorClass}
128+ key={`info-${ship}-master-2`}
129+ col1={`Master Ticket Shard 2 of 3`}
130+ col2={''}
131+ />,
132+ <Tile
133+ colorClass={colorClass}
134+ key={`info-${ship}-master-3`}
135+ col1={`Master Ticket Shard 3 of 3`}
136+ col2={''}
137+ />,
105138 ];
106139};
107140
108−
109−
110−const Tile = (props) => {
141+const Tile = props => {
111142 return (
112143 <div
113− className={ `${props.colorClass} white row pl-4 text-mono` }
114− style={{borderBottom:'2px solid black', height: '39px' }}
144+ className={`${props.colorClass} white row pl-4 text-mono`}
145+ style={{ borderBottom: '2px solid black', height: '39px' }}
115146 key={props.key}>
116−
117147 <div className={'col-md-8 flex align-center text-600'}>{props.col1}</div>
118148 <div className={'col-md-4 flex align-center text-500'}>{props.col2}</div>
119149 </div>
120− )
121−}
122−
150+ );
151+};
123152
124−const galaxyInfo = (ship, sig) =>
125− <div className={'mt-8'} key={ 'wallet-' + ship }>
126− { infoHeader(ship) }
127− <div className={'row'} key={ ship + '-body' }>
128− { infoSigil(ship, sig) }
129− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
130− { infoMasterTicketShard(ship) }
131− { infoSpawnSeed(ship) }
132− { infoManagementSeed(ship) }
133− { infoVotingSeed(ship) }
153+const galaxyInfo = (ship, sig) => (
154+ <div className={'mt-8'} key={'wallet-' + ship}>
155+ {infoHeader(ship)}
156+ <div className={'row'} key={ship + '-body'}>
157+ {infoSigil(ship, sig)}
158+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
159+ {infoMasterTicketShard(ship)}
160+ {infoSpawnSeed(ship)}
161+ {infoManagementSeed(ship)}
162+ {infoVotingSeed(ship)}
134163 </div>
135164 </div>
136165 </div>
137−
138−const starInfo = (ship, sig) =>
139− <div className={'mt-8'} key={ 'wallet-' + ship }>
140− { infoHeader(ship) }
141− <div className={'row'} key={ ship + '-body' }>
142− { infoSigil(ship, sig) }
143− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
144− { infoMasterTicket(ship) }
145− { infoSpawnSeed(ship) }
146− { infoManagementSeed(ship) }
166+);
167+
168+const starInfo = (ship, sig) => (
169+ <div className={'mt-8'} key={'wallet-' + ship}>
170+ {infoHeader(ship)}
171+ <div className={'row'} key={ship + '-body'}>
172+ {infoSigil(ship, sig)}
173+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
174+ {infoMasterTicket(ship)}
175+ {infoSpawnSeed(ship)}
176+ {infoManagementSeed(ship)}
147177 </div>
148178 </div>
149179 </div>
150−
151−const planetInfo = (ship, sig) =>
152− <div className={'mt-8'} key={ 'wallet-' + ship }>
153− { infoHeader(ship) }
154− <div className={'row'} key={ ship + '-body' }>
155− { infoSigil(ship, sig) }
156− <div className={ 'col-md-7 ml-2' } key={ 'info-' + ship }>
157− { infoMasterTicket(ship) }
158− { infoManagementSeed(ship) }
180+);
181+
182+const planetInfo = (ship, sig) => (
183+ <div className={'mt-8'} key={'wallet-' + ship}>
184+ {infoHeader(ship)}
185+ <div className={'row'} key={ship + '-body'}>
186+ {infoSigil(ship, sig)}
187+ <div className={'col-md-7 ml-2'} key={'info-' + ship}>
188+ {infoMasterTicket(ship)}
189+ {infoManagementSeed(ship)}
159190 </div>
160191 </div>
161192 </div>
193+);
162194
163195class Generate extends React.Component {
164196 constructor(props) {
165− super(props)
197+ super(props);
166198 this.handleGenerate = this.handleGenerate.bind(this);
167199 }
168200
@@ -205,16 +237,17 @@ class Generate extends React.Component {
205237
206238 const wallets = await Promise.all(walletPromises);
207239
208− const reshapedWallet = wallets.reduce((acc, {ship, wallet}) => ({...acc, [ship]: wallet }), {})
240+ const reshapedWallet = wallets.reduce(
241+ (acc, { ship, wallet }) => ({ ...acc, [ship]: wallet }),
242+ {}
243+ );
209244
210245 setGlobalState({
211246 'wallets.value': reshapedWallet,
212247 'wallets.state': GEN_STATES.DERIVATION_SUCCESS,
213248 'paperCollateral.state': GEN_STATES.PAPER_PENDING,
214249 });
215− }
216−
217−
250+ };
218251
219252 forciblyExecuteArgon2Once = async enyTable => {
220253 // NB (jtobin):
@@ -222,15 +255,17 @@ class Generate extends React.Component {
222255 // following is some insane thing required so as not to run into bizarre
223256 // emscripten errors
224257 if (enyTable.length === 0) {
225− throw new Error('no ship information provided')
258+ throw new Error('no ship information provided');
226259 } else {
227− let throwawayEntry = enyTable[0]
260+ let throwawayEntry = enyTable[0];
228261 // eslint-disable-next-line
229− let throwaway = await genWallet(throwawayEntry.ship, throwawayEntry.ticket, () => {})
230− };
231− }
232−
233−
262+ let throwaway = await genWallet(
263+ throwawayEntry.ship,
264+ throwawayEntry.ticket,
265+ () => {}
266+ );
267+ }
268+ };
234269
235270 render() {
236271 const { props } = this;
@@ -255,26 +290,17 @@ class Generate extends React.Component {
255290 const ws = props['wallets.state'];
256291 const ps = props['paperCollateral.state'];
257292
258−
259293 const ALL_SUCCESS =
260− es === ENY_SUCCESS &&
261− ws === DERIVATION_SUCCESS &&
262− ps === PAPER_SUCCESS;
294+ es === ENY_SUCCESS && ws === DERIVATION_SUCCESS && ps === PAPER_SUCCESS;
263295
264296 const NONE_START =
265− es === ENY_NOSTART &&
266− ws === DERIVATION_NOSTART &&
267− ps === PAPER_NOSTART;
297+ es === ENY_NOSTART && ws === DERIVATION_NOSTART && ps === PAPER_NOSTART;
268298
269299 const ANY_ERROR =
270− es === ENY_FAILURE ||
271− ws === DERIVATION_FAILURE ||
272− ps === PAPER_FAILURE;
300+ es === ENY_FAILURE || ws === DERIVATION_FAILURE || ps === PAPER_FAILURE;
273301
274302 const ANY_PENDING =
275− es === ENY_PENDING ||
276− ws === DERIVATION_PENDING ||
277− ps === PAPER_PENDING;
303+ es === ENY_PENDING || ws === DERIVATION_PENDING || ps === PAPER_PENDING;
278304
279305 const genButtonClass = classnames({
280306 'bg-blue white': true,
@@ -324,7 +350,7 @@ class Generate extends React.Component {
324350 if (ALL_SUCCESS === true) r = 'Success.';
325351 if (ANY_ERROR === true) r = 'Try Again';
326352 return r;
327− }
353+ };
328354
329355 const disableGenerateButton = isGenerateButtonDisabled();
330356 const genText = getGenButtonText();
@@ -332,72 +358,86 @@ class Generate extends React.Component {
332358 const nextButtonClass = classnames({
333359 'bg-blue white': ALL_SUCCESS === true,
334360 'bg-gray black': ALL_SUCCESS !== true,
335− })
361+ });
336362
337363 const nextButtonDisabled = ws !== ALL_SUCCESS;
338364
339− return(
365+ return (
340366 <div className={'row'}>
341− <div className={'col-md-8'}>
342367 <div className={'col-md-8'}>
343− <h2>{`Review your paper wallet storage recommendations.`}</h2>
344− <p>
345− {`Each ship is placed into its own wallet with proxies that differ in
368+ <div className={'col-md-8'}>
369+ <h2>{`Review your paper wallet storage recommendations.`}</h2>
370+ <p>
371+ {`Each ship is placed into its own wallet with proxies that differ in
346372 importance and security requirements. These are listed as examples below. A key to our security recommendations can be found to the right of the page.`}
347− </p>
348− </div>
373+ </p>
374+ </div>
349375
350− { props['profile.value'].galaxies.map(ship => shipInfo(ship)) }
351− { props['profile.value'].stars.map(ship => shipInfo(ship)) }
352− { props['profile.value'].planets.map(ship => shipInfo(ship)) }
376+ {props['profile.value'].galaxies.map(ship => shipInfo(ship))}
377+ {props['profile.value'].stars.map(ship => shipInfo(ship))}
378+ {props['profile.value'].planets.map(ship => shipInfo(ship))}
379+ </div>
353380
354− </div>
381+ <div className="col-md-3 col-md-offset-1 mt-9">
382+ <div className="fixed" style={{ width: '230px' }}>
383+ <div className="col-md-12 bg-very-high h-2 mt-4"></div>
384+ <h4 className="text-600 mt-2">Very High Friction</h4>
385+ <p className="text-sm" style={{ lineHeight: '16px' }}>
386+ Extremely valuable. Cold storage on a 3 part sharded paper wallet.
387+ Store each shard in a separate geographic location with protection
388+ against fire, water and other elements.
389+ </p>
390+ <div className="col-md-12 bg-high h-2 mt-4"></div>
391+ <h4 className="text-600 mt-2">High Friction</h4>
392+ <p className="text-sm" style={{ lineHeight: '16px' }}>
393+ Very valuable. Cold storage on a paper wallet in a different
394+ location than your computer, such as a safe.
395+ </p>
396+ <div className="col-md-12 bg-medium h-2 mt-4"></div>
397+ <h4 className="text-600 mt-2">Medium Friction</h4>
398+ <p className="text-sm" style={{ lineHeight: '16px' }}>
399+ Store cold in a paper wallet; can be stored in a desk drawer in a
400+ trusted environment.
401+ </p>
402+ <div className="col-md-12 bg-low h-2 mt-4"></div>
403+ <h4 className="text-600 mt-2">Low Friction</h4>
404+ <p className="text-sm" style={{ lineHeight: '16px' }}>
405+ Ok to store hot on a trusted networked computer.
406+ </p>
407+ </div>
408+ </div>
355409
356− <div className="col-md-3 col-md-offset-1 mt-9">
357− <div className="fixed" style={{width: '230px'}}>
358− <div className="col-md-12 bg-very-high h-2 mt-4"></div>
359− <h4 className="text-600 mt-2">Very High Friction</h4>
360− <p className="text-sm" style={{lineHeight: '16px'}}>
361− Extremely valuable. Cold storage on a 3 part sharded paper wallet. Store each shard in a separate geographic location with protection against fire, water and other elements.
410+ <div className={'col-md-9 btn-tray col-'}>
411+ <h3>{"Got it? Then it's time to generate your wallets."}</h3>
412+ <p className={'col-md-8'}>
413+ {
414+ 'If you have a large number of ships, this may take several minutes and the page may hang or become unresponsive. If you close this browser window, you will have to start from the beginning.'
415+ }
362416 </p>
363− <div className="col-md-12 bg-high h-2 mt-4"></div>
364− <h4 className="text-600 mt-2">High Friction</h4>
365− <p className="text-sm" style={{lineHeight: '16px'}}>
366− Very valuable. Cold storage on a paper wallet in a different location than your computer, such as a safe.
367− </p>
368− <div className="col-md-12 bg-medium h-2 mt-4"></div>
369− <h4 className="text-600 mt-2">Medium Friction</h4>
370− <p className="text-sm" style={{lineHeight: '16px'}}>
371− Store cold in a paper wallet; can be stored in a desk drawer in a trusted environment.
372− </p>
373− <div className="col-md-12 bg-low h-2 mt-4"></div>
374− <h4 className="text-600 mt-2">Low Friction</h4>
375− <p className="text-sm" style={{lineHeight: '16px'}}>
376− Ok to store hot on a trusted networked computer.
377− </p>
378− </div>
379− </div>
380417
381− <div className={'col-md-9 btn-tray col-'}>
382− <h3>{"Got it? Then it's time to generate your wallets."}</h3>
383− <p className={'col-md-8'}>{"If you have a large number of ships, this may take several minutes and the page may hang or become unresponsive. If you close this browser window, you will have to start from the beginning."}</p>
384−
385− <div className={'flex h-12 mt-8 mb-4'}>
386− <Button
387− className={ `${genButtonClass} b-r-1-black w-50 m-0` }
388− text={genText}
389− onClick={ this.handleGenerate }
390− disable={disableGenerateButton} />
391− <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
392− <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
393− <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
394− <Button
395− className={ `${nextButtonClass} m-0` }
396− text={'View wallets →'}
397− disable={ nextButtonDisabled }
398− onClick= { () => setGlobalState({ route: '/Download' , 'currentStep': NEXT_STEP_NUM })} />
418+ <div className={'flex h-12 mt-8 mb-4'}>
419+ <Button
420+ className={`${genButtonClass} b-r-1-black w-50 m-0`}
421+ text={genText}
422+ onClick={this.handleGenerate}
423+ disable={disableGenerateButton}
424+ />
425+ <div className={`${entropyBarClassnames} f-1 b-r-1-black`} />
426+ <div className={`${walletsBarClassnames} f-3 b-r-1-black`} />
427+ <div className={`${paperBarClassnames} f-3 b-r-1-black`} />
428+ <Button
429+ className={`${nextButtonClass} m-0`}
430+ text={'View wallets →'}
431+ disable={nextButtonDisabled}
432+ onClick={() =>
433+ setGlobalState({
434+ route: '/Download',
435+ currentStep: NEXT_STEP_NUM,
436+ })
437+ }
438+ />
439+ </div>
399440 </div>
400− </div>
401441
402442 <div className={'extremely-hidden'}>
403443 <div className={'text-300'}>{'text-300'}</div>
@@ -410,21 +450,24 @@ class Generate extends React.Component {
410450 <div className={'text-600 h-font'}>{'text-600 h-font'}</div>
411451 </div>
412452
413− { props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS
414− ? <PaperCollateralRenderer
453+ {props['wallets.state'] === GEN_STATES.DERIVATION_SUCCESS ? (
454+ <PaperCollateralRenderer
415455 wallet={props['wallets.value']}
416456 className={'extremely-hidden'}
417− callback={data => setGlobalState({
418− 'paperCollateral.value': data,
419− 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS
420− })}
421− mode={'REGISTRATION'} />
422− : <div />
423− }
424−
457+ callback={data =>
458+ setGlobalState({
459+ 'paperCollateral.value': data,
460+ 'paperCollateral.state': GEN_STATES.PAPER_SUCCESS,
461+ })
462+ }
463+ mode={'REGISTRATION'}
464+ />
465+ ) : (
466+ <div />
467+ )}
425468 </div>
426− )
469+ );
427470 }
428471}
429472
430−export default Generate
473+export default Generate;
src/walletgen/views/NetworkWarning.js+6−6
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
22
3−const NetworkWarning = (props) =>
3+const NetworkWarning = props => (
44 <div className={'col-md-6'}>
55 <h2>{'You may not use this tool while connected to a network.'}</h2>
66
77 <p>
8− To continue, disconnect from the internet and refresh the page. If you highly value your ships,
9− we recommend you use a new, airgapped machine.
8+ To continue, disconnect from the internet and refresh the page. If you
9+ highly value your ships, we recommend you use a new, airgapped machine.
1010 </p>
11−
1211 </div>
12+);
1313
14−export default NetworkWarning
14+export default NetworkWarning;
src/walletgen/views/PrivacyPolicy.js+229−305
@@ -1,443 +1,367 @@
1−import React from 'react'
1+import React from 'react';
22
3−import Button from '../components/Button'
3+import Button from '../components/Button';
44
55class PrivacyPolicy extends React.Component {
6−
76 render() {
8− const { setGlobalState, lastRoute } = this.props
7+ const { setGlobalState, lastRoute } = this.props;
98
109 return (
1110 <div>
11+ <p>Effective date: June 01, 2018</p>
1212 <p>
13− Effective date: June 01, 2018
14− </p>
15− <p>
16− Tlon Corporation ("us", "we", or "our") operates the website and the Urbit
17− mobile application (the "Service").
18− </p>
19− <p>
20− This page informs you of our policies regarding the collection, use, and
21− disclosure of personal data when you use our Service and the choices you have
22− associated with that data.
23− </p>
24− <p>
25− We use your data to provide and improve the Service. By using the Service, you
26− agree to the collection and use of information in accordance with this policy.
27− Unless otherwise defined in this Privacy Policy, terms used in this Privacy
28− Policy have the same meanings as in our Terms and Conditions.
29− </p>
30− <p>
31− Definitions
32− </p>
33− <p>
34− Service
35− </p>
36− <p>
37− Service means the website and the Urbit mobile application operated by Tlon
38− Corporation
39− </p>
40− <p>
41− Personal Data
42− </p>
43− <p>
44− Personal Data means data about a living individual who can be identified from
45− those data (or from those and other information either in our possession or
46− likely to come into our possession).
47− </p>
48− <p>
49− Usage Data
50− </p>
51− <p>
52− Usage Data is data collected automatically either generated by the use of the
53− Service or from the Service infrastructure itself (for example, the duration of
54− a page visit).
55− </p>
56− <p>
57− Cookies
58− </p>
59− <p>
60− Cookies are small pieces of data stored on your device (computer or mobile
61− device).
62− </p>
63− <p>
64− Data Controller
65− </p>
66− <p>
67− Data Controller means the natural or legal person who (either alone or jointly
68− or in common with other persons) determines the purposes for which and the
69− manner in which any personal information are, or are to be, processed.
70− </p>
71− <p>
72− For the purpose of this Privacy Policy, we are a Data Controller of your
73− Personal Data.
74− </p>
75− <p>
76− Data Processors (or Service Providers)
77− </p>
78− <p>
79− Data Processor (or Service Provider) means any natural or legal person who
80− processes the data on behalf of the Data Controller.
81− </p>
82− <p>
83− We may use the services of various Service Providers in order to process your
84− data more effectively.
85− </p>
86− <p>
87− Data Subject (or User)
88− </p>
89− <p>
90− Data Subject is any living individual who is using our Service and is the
91− subject of Personal Data.
92− </p>
93− <p>
94− Information Collection and Use
95− </p>
96− <p>
97− We collect several different types of information for various purposes to
98− provide and improve our Service to you.
99− </p>
100− <p>
101− Types of Data Collected
13+ Tlon Corporation ("us", "we", or "our") operates the website and the
14+ Urbit mobile application (the "Service").
10215 </p>
10316 <p>
104− Personal Data
17+ This page informs you of our policies regarding the collection, use,
18+ and disclosure of personal data when you use our Service and the
19+ choices you have associated with that data.
10520 </p>
10621 <p>
107− While using our Service, we may ask you to provide us with certain personally
108− identifiable information that can be used to contact or identify you ("Personal
109− Data"). Personally identifiable information may include, but is not limited to:
22+ We use your data to provide and improve the Service. By using the
23+ Service, you agree to the collection and use of information in
24+ accordance with this policy. Unless otherwise defined in this Privacy
25+ Policy, terms used in this Privacy Policy have the same meanings as in
26+ our Terms and Conditions.
11027 </p>
28+ <p>Definitions</p>
29+ <p>Service</p>
11130 <p>
112− First name and last name
31+ Service means the website and the Urbit mobile application operated by
32+ Tlon Corporation
11333 </p>
34+ <p>Personal Data</p>
11435 <p>
115− Cookies and Usage Data
36+ Personal Data means data about a living individual who can be
37+ identified from those data (or from those and other information either
38+ in our possession or likely to come into our possession).
11639 </p>
40+ <p>Usage Data</p>
11741 <p>
118− Usage Data
42+ Usage Data is data collected automatically either generated by the use
43+ of the Service or from the Service infrastructure itself (for example,
44+ the duration of a page visit).
11945 </p>
46+ <p>Cookies</p>
12047 <p>
121− We may also collect information that your browser sends whenever you visit our
122− Service or when you access the Service by or through a mobile device ("Usage
123− Data").
48+ Cookies are small pieces of data stored on your device (computer or
49+ mobile device).
12450 </p>
51+ <p>Data Controller</p>
12552 <p>
126− This Usage Data may include information such as your computer's Internet
127− Protocol address (e.g. IP address), browser type, browser version, the pages of
128− our Service that you visit, the time and date of your visit, the time spent on
129− those pages, unique device identifiers and other diagnostic data.
53+ Data Controller means the natural or legal person who (either alone or
54+ jointly or in common with other persons) determines the purposes for
55+ which and the manner in which any personal information are, or are to
56+ be, processed.
13057 </p>
13158 <p>
132− When you access the Service by or through a mobile device, this Usage Data may
133− include information such as the type of mobile device you use, your mobile
134− device unique ID, the IP address of your mobile device, your mobile operating
135− system, the type of mobile Internet browser you use, unique device identifiers
136− and other diagnostic data.
59+ For the purpose of this Privacy Policy, we are a Data Controller of
60+ your Personal Data.
13761 </p>
62+ <p>Data Processors (or Service Providers)</p>
13863 <p>
139− Tracking & Cookies Data
64+ Data Processor (or Service Provider) means any natural or legal person
65+ who processes the data on behalf of the Data Controller.
14066 </p>
14167 <p>
142− We use cookies and similar tracking technologies to track the activity on our
143− Service and hold certain information.
68+ We may use the services of various Service Providers in order to
69+ process your data more effectively.
14470 </p>
71+ <p>Data Subject (or User)</p>
14572 <p>
146− Cookies are files with small amount of data which may include an anonymous
147− unique identifier. Cookies are sent to your browser from a website and stored
148− on your device. Tracking technologies also used are beacons, tags, and scripts
149− to collect and track information and to improve and analyze our Service.
73+ Data Subject is any living individual who is using our Service and is
74+ the subject of Personal Data.
15075 </p>
76+ <p>Information Collection and Use</p>
15177 <p>
152− You can instruct your browser to refuse all cookies or to indicate when a
153− cookie is being sent. However, if you do not accept cookies, you may not be
154− able to use some portions of our Service.
78+ We collect several different types of information for various purposes
79+ to provide and improve our Service to you.
15580 </p>
81+ <p>Types of Data Collected</p>
82+ <p>Personal Data</p>
15683 <p>
157− Examples of Cookies we use:
84+ While using our Service, we may ask you to provide us with certain
85+ personally identifiable information that can be used to contact or
86+ identify you ("Personal Data"). Personally identifiable information
87+ may include, but is not limited to:
15888 </p>
89+ <p>First name and last name</p>
90+ <p>Cookies and Usage Data</p>
91+ <p>Usage Data</p>
15992 <p>
160− Session Cookies. We use Session Cookies to operate our Service.
93+ We may also collect information that your browser sends whenever you
94+ visit our Service or when you access the Service by or through a
95+ mobile device ("Usage Data").
16196 </p>
16297 <p>
163− Preference Cookies. We use Preference Cookies to remember your preferences and
164− various settings.
98+ This Usage Data may include information such as your computer's
99+ Internet Protocol address (e.g. IP address), browser type, browser
100+ version, the pages of our Service that you visit, the time and date of
101+ your visit, the time spent on those pages, unique device identifiers
102+ and other diagnostic data.
165103 </p>
166104 <p>
167− Security Cookies. We use Security Cookies for security purposes.
105+ When you access the Service by or through a mobile device, this Usage
106+ Data may include information such as the type of mobile device you
107+ use, your mobile device unique ID, the IP address of your mobile
108+ device, your mobile operating system, the type of mobile Internet
109+ browser you use, unique device identifiers and other diagnostic data.
168110 </p>
111+ <p>Tracking & Cookies Data</p>
169112 <p>
170− Use of Data
113+ We use cookies and similar tracking technologies to track the activity
114+ on our Service and hold certain information.
171115 </p>
172116 <p>
173− Tlon Corporation uses the collected data for various purposes:
117+ Cookies are files with small amount of data which may include an
118+ anonymous unique identifier. Cookies are sent to your browser from a
119+ website and stored on your device. Tracking technologies also used are
120+ beacons, tags, and scripts to collect and track information and to
121+ improve and analyze our Service.
174122 </p>
175123 <p>
176− To provide and maintain our Service
124+ You can instruct your browser to refuse all cookies or to indicate
125+ when a cookie is being sent. However, if you do not accept cookies,
126+ you may not be able to use some portions of our Service.
177127 </p>
128+ <p>Examples of Cookies we use:</p>
129+ <p>Session Cookies. We use Session Cookies to operate our Service.</p>
178130 <p>
179− To notify you about changes to our Service
131+ Preference Cookies. We use Preference Cookies to remember your
132+ preferences and various settings.
180133 </p>
134+ <p>Security Cookies. We use Security Cookies for security purposes.</p>
135+ <p>Use of Data</p>
136+ <p>Tlon Corporation uses the collected data for various purposes:</p>
137+ <p>To provide and maintain our Service</p>
138+ <p>To notify you about changes to our Service</p>
181139 <p>
182− To allow you to participate in interactive features of our Service when you
183− choose to do so
140+ To allow you to participate in interactive features of our Service
141+ when you choose to do so
184142 </p>
143+ <p>To provide customer support</p>
185144 <p>
186− To provide customer support
145+ To gather analysis or valuable information so that we can improve our
146+ Service
187147 </p>
148+ <p>To monitor the usage of our Service</p>
149+ <p>To detect, prevent and address technical issues</p>
188150 <p>
189− To gather analysis or valuable information so that we can improve our Service
151+ Legal Basis for Processing Personal Data Under General Data Protection
152+ Regulation (GDPR)
190153 </p>
191154 <p>
192− To monitor the usage of our Service
155+ If you are from the European Economic Area (EEA), Tlon Corporation
156+ legal basis for collecting and using the personal information
157+ described in this Privacy Policy depends on the Personal Data we
158+ collect and the specific context in which we collect it.
193159 </p>
160+ <p>Tlon Corporation may process your Personal Data because:</p>
161+ <p>We need to perform a contract with you</p>
162+ <p>You have given us permission to do so</p>
194163 <p>
195− To detect, prevent and address technical issues
164+ The processing is in our legitimate interests and it's not overridden
165+ by your rights
196166 </p>
167+ <p>To comply with the law</p>
168+ <p>Retention of Data</p>
197169 <p>
198− Legal Basis for Processing Personal Data Under General Data Protection
199− Regulation (GDPR)
170+ Tlon Corporation will retain your Personal Data only for as long as is
171+ necessary for the purposes set out in this Privacy Policy. We will
172+ retain and use your Personal Data to the extent necessary to comply
173+ with our legal obligations (for example, if we are required to retain
174+ your data to comply with applicable laws), resolve disputes, and
175+ enforce our legal agreements and policies.
200176 </p>
201177 <p>
202− If you are from the European Economic Area (EEA), Tlon Corporation legal basis
203− for collecting and using the personal information described in this Privacy
204− Policy depends on the Personal Data we collect and the specific context in
205− which we collect it.
178+ Tlon Corporation will also retain Usage Data for internal analysis
179+ purposes. Usage Data is generally retained for a shorter period of
180+ time, except when this data is used to strengthen the security or to
181+ improve the functionality of our Service, or we are legally obligated
182+ to retain this data for longer time periods.
206183 </p>
184+ <p>Transfer of Data</p>
207185 <p>
208− Tlon Corporation may process your Personal Data because:
186+ Your information, including Personal Data, may be transferred to — and
187+ maintained on — computers located outside of your state, province,
188+ country or other governmental jurisdiction where the data protection
189+ laws may differ than those from your jurisdiction.
209190 </p>
210191 <p>
211− We need to perform a contract with you
192+ If you are located outside United States and choose to provide
193+ information to us, please note that we transfer the data, including
194+ Personal Data, to United States and process it there.
212195 </p>
213196 <p>
214− You have given us permission to do so
197+ Your consent to this Privacy Policy followed by your submission of
198+ such information represents your agreement to that transfer.
215199 </p>
216200 <p>
217− The processing is in our legitimate interests and it's not overridden by your
218− rights
201+ Tlon Corporation will take all steps reasonably necessary to ensure
202+ that your data is treated securely and in accordance with this Privacy
203+ Policy and no transfer of your Personal Data will take place to an
204+ organization or a country unless there are adequate controls in place
205+ including the security of your data and other personal information.
219206 </p>
207+ <p>Disclosure of Data</p>
208+ <p>Legal Requirements</p>
220209 <p>
221− To comply with the law
210+ Tlon Corporation may disclose your Personal Data in the good faith
211+ belief that such action is necessary to:
222212 </p>
213+ <p>To comply with a legal obligation</p>
214+ <p>To protect and defend the rights or property of Tlon Corporation</p>
223215 <p>
224− Retention of Data
216+ To prevent or investigate possible wrongdoing in connection with the
217+ Service
225218 </p>
226219 <p>
227− Tlon Corporation will retain your Personal Data only for as long as is
228− necessary for the purposes set out in this Privacy Policy. We will retain and
229− use your Personal Data to the extent necessary to comply with our legal
230− obligations (for example, if we are required to retain your data to comply with
231− applicable laws), resolve disputes, and enforce our legal agreements and
232− policies.
220+ To protect the personal safety of users of the Service or the public
233221 </p>
222+ <p>To protect against legal liability</p>
223+ <p>Security of Data</p>
234224 <p>
235− Tlon Corporation will also retain Usage Data for internal analysis purposes.
236− Usage Data is generally retained for a shorter period of time, except when this
237− data is used to strengthen the security or to improve the functionality of our
238− Service, or we are legally obligated to retain this data for longer time
239− periods.
225+ The security of your data is important to us, but remember that no
226+ method of transmission over the Internet, or method of electronic
227+ storage is 100% secure. While we strive to use commercially acceptable
228+ means to protect your Personal Data, we cannot guarantee its absolute
229+ security.
240230 </p>
231+ <p>"Do Not Track" Signals</p>
241232 <p>
242− Transfer of Data
233+ We do not support Do Not Track ("DNT"). Do Not Track is a preference
234+ you can set in your web browser to inform websites that you do not
235+ want to be tracked.
243236 </p>
244237 <p>
245− Your information, including Personal Data, may be transferred to — and
246− maintained on — computers located outside of your state, province, country or
247− other governmental jurisdiction where the data protection laws may differ than
248− those from your jurisdiction.
238+ You can enable or disable Do Not Track by visiting the Preferences or
239+ Settings page of your web browser.
249240 </p>
250241 <p>
251− If you are located outside United States and choose to provide information to
252− us, please note that we transfer the data, including Personal Data, to United
253− States and process it there.
242+ Your Data Protection Rights Under General Data Protection Regulation
243+ (GDPR)
254244 </p>
255245 <p>
256− Your consent to this Privacy Policy followed by your submission of such
257− information represents your agreement to that transfer.
246+ If you are a resident of the European Economic Area (EEA), you have
247+ certain data protection rights. Tlon Corporation aims to take
248+ reasonable steps to allow you to correct, amend, delete, or limit the
249+ use of your Personal Data.
258250 </p>
259251 <p>
260− Tlon Corporation will take all steps reasonably necessary to ensure that your
261− data is treated securely and in accordance with this Privacy Policy and no
262− transfer of your Personal Data will take place to an organization or a country
263− unless there are adequate controls in place including the security of your data
264− and other personal information.
252+ If you wish to be informed what Personal Data we hold about you and if
253+ you want it to be removed from our systems, please contact us.
265254 </p>
266255 <p>
267− Disclosure of Data
256+ In certain circumstances, you have the following data protection
257+ rights:
268258 </p>
269259 <p>
270− Legal Requirements
260+ The right to access, update or to delete the information we have on
261+ you. Whenever made possible, you can access, update or request
262+ deletion of your Personal Data directly within your account settings
263+ section. If you are unable to perform these actions yourself, please
264+ contact us to assist you.
271265 </p>
272266 <p>
273− Tlon Corporation may disclose your Personal Data in the good faith belief that
274− such action is necessary to:
267+ The right of rectification. You have the right to have your
268+ information rectified if that information is inaccurate or incomplete.
275269 </p>
276270 <p>
277− To comply with a legal obligation
271+ The right to object. You have the right to object to our processing of
272+ your Personal Data.
278273 </p>
279274 <p>
280− To protect and defend the rights or property of Tlon Corporation
275+ The right of restriction. You have the right to request that we
276+ restrict the processing of your personal information.
281277 </p>
282278 <p>
283− To prevent or investigate possible wrongdoing in connection with the Service
279+ The right to data portability. You have the right to be provided with
280+ a copy of the information we have on you in a structured,
281+ machine-readable and commonly used format.
284282 </p>
285283 <p>
286− To protect the personal safety of users of the Service or the public
284+ The right to withdraw consent. You also have the right to withdraw
285+ your consent at any time where Tlon Corporation relied on your consent
286+ to process your personal information.
287287 </p>
288288 <p>
289− To protect against legal liability
289+ Please note that we may ask you to verify your identity before
290+ responding to such requests.
290291 </p>
291292 <p>
292− Security of Data
293+ You have the right to complain to a Data Protection Authority about
294+ our collection and use of your Personal Data. For more information,
295+ please contact your local data protection authority in the European
296+ Economic Area (EEA).
293297 </p>
298+ <p>Service Providers</p>
294299 <p>
295− The security of your data is important to us, but remember that no method of
296− transmission over the Internet, or method of electronic storage is 100% secure.
297− While we strive to use commercially acceptable means to protect your Personal
298− Data, we cannot guarantee its absolute security.
300+ We may employ third party companies and individuals to facilitate our
301+ Service ("Service Providers"), to provide the Service on our behalf,
302+ to perform Service-related services or to assist us in analyzing how
303+ our Service is used.
299304 </p>
300305 <p>
301− "Do Not Track" Signals
306+ These third parties have access to your Personal Data only to perform
307+ these tasks on our behalf and are obligated not to disclose or use it
308+ for any other purpose.
302309 </p>
310+ <p>Links to Other Sites</p>
303311 <p>
304− We do not support Do Not Track ("DNT"). Do Not Track is a preference you can
305− set in your web browser to inform websites that you do not want to be tracked.
312+ Our Service may contain links to other sites that are not operated by
313+ us. If you click on a third party link, you will be directed to that
314+ third party's site. We strongly advise you to review the Privacy
315+ Policy of every site you visit.
306316 </p>
307317 <p>
308− You can enable or disable Do Not Track by visiting the Preferences or Settings
309− page of your web browser.
318+ We have no control over and assume no responsibility for the content,
319+ privacy policies or practices of any third party sites or services.
310320 </p>
321+ <p>Children's Privacy</p>
311322 <p>
312− Your Data Protection Rights Under General Data Protection Regulation (GDPR)
323+ Our Service does not address anyone under the age of 18 ("Children").
313324 </p>
314325 <p>
315− If you are a resident of the European Economic Area (EEA), you have certain
316− data protection rights. Tlon Corporation aims to take reasonable steps to allow
317− you to correct, amend, delete, or limit the use of your Personal Data.
326+ We do not knowingly collect personally identifiable information from
327+ anyone under the age of 18. If you are a parent or guardian and you
328+ are aware that your child has provided us with Personal Data, please
329+ contact us. If we become aware that we have collected Personal Data
330+ from children without verification of parental consent, we take steps
331+ to remove that information from our servers.
318332 </p>
333+ <p>Changes to This Privacy Policy</p>
319334 <p>
320− If you wish to be informed what Personal Data we hold about you and if you want
321− it to be removed from our systems, please contact us.
335+ We may update our Privacy Policy from time to time. We will notify you
336+ of any changes by posting the new Privacy Policy on this page.
322337 </p>
323338 <p>
324− In certain circumstances, you have the following data protection rights:
339+ We will let you know via email and/or a prominent notice on our
340+ Service, prior to the change becoming effective and update the
341+ "effective date" at the top of this Privacy Policy.
325342 </p>
326343 <p>
327− The right to access, update or to delete the information we have on you.
328− Whenever made possible, you can access, update or request deletion of your
329− Personal Data directly within your account settings section. If you are unable
330− to perform these actions yourself, please contact us to assist you.
344+ You are advised to review this Privacy Policy periodically for any
345+ changes. Changes to this Privacy Policy are effective when they are
346+ posted on this page.
331347 </p>
348+ <p>Contact Us</p>
332349 <p>
333− The right of rectification. You have the right to have your information
334− rectified if that information is inaccurate or incomplete.
335− </p>
336− <p>
337− The right to object. You have the right to object to our processing of your
338− Personal Data.
339− </p>
340− <p>
341− The right of restriction. You have the right to request that we restrict the
342− processing of your personal information.
343− </p>
344− <p>
345− The right to data portability. You have the right to be provided with a copy of
346− the information we have on you in a structured, machine-readable and commonly
347− used format.
348− </p>
349− <p>
350− The right to withdraw consent. You also have the right to withdraw your consent
351− at any time where Tlon Corporation relied on your consent to process your
352− personal information.
353− </p>
354− <p>
355− Please note that we may ask you to verify your identity before responding to
356− such requests.
357− </p>
358− <p>
359− You have the right to complain to a Data Protection Authority about our
360− collection and use of your Personal Data. For more information, please contact
361− your local data protection authority in the European Economic Area (EEA).
362− </p>
363− <p>
364− Service Providers
365− </p>
366− <p>
367− We may employ third party companies and individuals to facilitate our Service
368− ("Service Providers"), to provide the Service on our behalf, to perform
369− Service-related services or to assist us in analyzing how our Service is used.
370− </p>
371− <p>
372− These third parties have access to your Personal Data only to perform these
373− tasks on our behalf and are obligated not to disclose or use it for any other
374− purpose.
375− </p>
376− <p>
377− Links to Other Sites
378− </p>
379− <p>
380− Our Service may contain links to other sites that are not operated by us. If
381− you click on a third party link, you will be directed to that third party's
382− site. We strongly advise you to review the Privacy Policy of every site you
383− visit.
384− </p>
385− <p>
386− We have no control over and assume no responsibility for the content, privacy
387− policies or practices of any third party sites or services.
388− </p>
389− <p>
390− Children's Privacy
391− </p>
392− <p>
393− Our Service does not address anyone under the age of 18 ("Children").
394− </p>
395− <p>
396− We do not knowingly collect personally identifiable information from anyone
397− under the age of 18. If you are a parent or guardian and you are aware that
398− your child has provided us with Personal Data, please contact us. If we become
399− aware that we have collected Personal Data from children without verification
400− of parental consent, we take steps to remove that information from our servers.
401− </p>
402− <p>
403− Changes to This Privacy Policy
404− </p>
405− <p>
406− We may update our Privacy Policy from time to time. We will notify you of any
407− changes by posting the new Privacy Policy on this page.
408− </p>
409− <p>
410− We will let you know via email and/or a prominent notice on our Service, prior
411− to the change becoming effective and update the "effective date" at the top of
412− this Privacy Policy.
413− </p>
414− <p>
415− You are advised to review this Privacy Policy periodically for any changes.
416− Changes to this Privacy Policy are effective when they are posted on this page.
417− </p>
418− <p>
419− Contact Us
420− </p>
421− <p>
422− If you have any questions about this Privacy Policy, please contact us:
423− </p>
424− <p>
425− By email: support@urbit.org
350+ If you have any questions about this Privacy Policy, please contact
351+ us:
426352 </p>
353+ <p>By email: support@urbit.org</p>
427354
428355 <div>
429−
430356 <Button
431357 className={'btn btn-primary'}
432358 text={'Close'}
433− onClick={ () => setGlobalState({ 'route': lastRoute })}
359+ onClick={() => setGlobalState({ route: lastRoute })}
434360 />
435−
436361 </div>
437−
438362 </div>
439− )
363+ );
440364 }
441365}
442366
443−export default PrivacyPolicy
367+export default PrivacyPolicy;
src/walletgen/views/Understand.js+37−14
@@ -1,6 +1,6 @@
1−import React from 'react'
1+import React from 'react';
22
3−import Button from '../components/Button'
3+import Button from '../components/Button';
44
55const NEXT_STEP_NUM = 4;
66
@@ -8,36 +8,59 @@ class Understand extends React.Component {
88 render() {
99 const { setGlobalState } = this.props;
1010
11− return(
11+ return (
1212 <div className={'col-md-6'}>
13− <h2 className={'mb-4'}>{"First, let's look at the HD Wallet in more detail."}</h2>
13+ <h2 className={'mb-4'}>
14+ {"First, let's look at the HD Wallet in more detail."}
15+ </h2>
1416
1517 <p>{`Urbit HD wallets contain the following important items.`}</p>
1618
1719 <h3 className={'mt-4 mb-0'}>Master Ticket</h3>
18− <p>Think of your master ticket like a very high value password. The master ticket is the secret code from which all of your other keys are derived. Technically, your master ticket is seed entropy. You should never share it with anyone, and store it very securely. This ticket can derive all of your other keys: your ownership key and all of the related proxies. </p>
20+ <p>
21+ Think of your master ticket like a very high value password. The
22+ master ticket is the secret code from which all of your other keys are
23+ derived. Technically, your master ticket is seed entropy. You should
24+ never share it with anyone, and store it very securely. This ticket
25+ can derive all of your other keys: your ownership key and all of the
26+ related proxies.{' '}
27+ </p>
1928 <h3 className={'mt-4 mb-0'}>Ownership Address</h3>
20− <p>An ownership address has all rights over the assets deeded to it. These rights are on-chain actions described and implemented in the Constitution, Urbit’s Ethereum smart contracts.</p>
29+ <p>
30+ An ownership address has all rights over the assets deeded to it.
31+ These rights are on-chain actions described and implemented in the
32+ Constitution, Urbit’s Ethereum smart contracts.
33+ </p>
2134 <h3 className={'mt-4 mb-0'}>Proxies</h3>
2235 <p>{`Proxy addresses allow you to execute non-ownership related actions like spawning child ships, voting, and setting networking keys without jeopardizing the keys you've designated with ownership rights. Setting proxy rights is optional, but it is recommended for on-chain actions you will execute more frequently.`}</p>
2336 <h3 className={'mt-4 mb-0 ml-8'}>Management Proxy</h3>
24− <p className={'ml-8'}>Can configure or set Urbit networking keys and conduct sponsorship related operations.</p>
37+ <p className={'ml-8'}>
38+ Can configure or set Urbit networking keys and conduct sponsorship
39+ related operations.
40+ </p>
2541 <h3 className={'mt-4 mb-0 ml-8'}>Voting Proxy</h3>
26− <p className={'ml-8'}>Galaxies only. Galaxies are the part of the galactic senate, and this means they can cast votes on new proposals including changes to the Constitution.</p>
42+ <p className={'ml-8'}>
43+ Galaxies only. Galaxies are the part of the galactic senate, and this
44+ means they can cast votes on new proposals including changes to the
45+ Constitution.
46+ </p>
2747 <h3 className={'mt-4 mb-0 ml-8'}>Spawn Proxy</h3>
28− <p className={'ml-8'}>For stars and galaxies only. Can create new child ships.</p>
48+ <p className={'ml-8'}>
49+ For stars and galaxies only. Can create new child ships.
50+ </p>
2951
3052 <div className={'btn-tray'}>
3153 <Button
3254 className={'btn btn-primary'}
33− text={"Ok, got it →"}
34− onClick={ () => setGlobalState({ route: '/Custody', 'currentStep': NEXT_STEP_NUM })}
55+ text={'Ok, got it →'}
56+ onClick={() =>
57+ setGlobalState({ route: '/Custody', currentStep: NEXT_STEP_NUM })
58+ }
3559 />
3660 </div>
37−
3861 </div>
39− )
62+ );
4063 }
4164}
4265
43−export default Understand
66+export default Understand;
src/walletgen/views/Upload.js+126−105
@@ -1,57 +1,62 @@
1−import React, { Component } from 'react'
1+import React, { Component } from 'react';
22
3−import Button from '../components/Button'
4−import UploadButton from '../components/UploadButton'
3+import Button from '../components/Button';
4+import UploadButton from '../components/UploadButton';
55
6−import { compose } from '../lib/lib'
7−
8−import { PROFILE_STATES } from '../lib/constants'
6+import { compose } from '../lib/lib';
97
8+import { PROFILE_STATES } from '../lib/constants';
109
1110const NEXT_STEP_NUM = 3;
1211
1312const validateBase64 = ({ data, pass, error }) => {
14− if (pass !== true) return { data, pass, error }
13+ if (pass !== true) return { data, pass, error };
1514
1615 try {
1716 atob(data);
18− return { data: atob(data), pass: true, error: '' }
19− } catch(e) {
20− console.error(e)
21− return { data: data, pass: false, error: 'File is not valid base64.' }
17+ return { data: atob(data), pass: true, error: '' };
18+ } catch (e) {
19+ console.error(e);
20+ return { data: data, pass: false, error: 'File is not valid base64.' };
2221 }
23−}
22+};
2423
2524const validateJSON = ({ data, pass, error }) => {
26− if (pass !== true) return { data, pass, error }
25+ if (pass !== true) return { data, pass, error };
2726
2827 try {
29− JSON.parse(data)
30− return { data: JSON.parse(data), pass: true, error: '' }
31− } catch(e) {
32− console.error(e)
33− return { data: data, pass: false, error: 'File does not decode into valid JSON.' }
28+ JSON.parse(data);
29+ return { data: JSON.parse(data), pass: true, error: '' };
30+ } catch (e) {
31+ console.error(e);
32+ return {
33+ data: data,
34+ pass: false,
35+ error: 'File does not decode into valid JSON.',
36+ };
3437 }
35−}
38+};
3639
3740const validateFile = ({ data, pass, error }) => {
3841 if (pass !== true) return { data, pass, error };
3942
4043 if (data.file.type !== 'text/plain') {
41− return { data: data, pass: false, error: 'File must be a .txt file' }
42− };
44+ return { data: data, pass: false, error: 'File must be a .txt file' };
45+ }
4346
4447 if (data.file.name.includes('urbit-ships') === false) {
45− return { data: data, pass: false, error: 'This does not appear to be the correct file.' }
46− };
48+ return {
49+ data: data,
50+ pass: false,
51+ error: 'This does not appear to be the correct file.',
52+ };
53+ }
4754
4855 return { data: data.event.target.result, pass, error };
49−
50−}
51−
56+};
5257
5358const validateObject = ({ data, pass, error }) => {
54− if (pass !== true) return { data, pass, error }
59+ if (pass !== true) return { data, pass, error };
5560
5661 const checkStringField = (obj, field) =>
5762 field in obj && typeof obj[field] === 'string';
@@ -60,20 +65,20 @@ const validateObject = ({ data, pass, error }) => {
6065 field in obj || Array.isArray(obj[field]);
6166
6267 const meetsRequirements = obj =>
63− checkStringField(obj,'idCode')
64− && checkShipField(obj,'planets')
65− && checkShipField(obj,'stars')
66− && checkShipField(obj,'galaxies');
67−
68+ checkStringField(obj, 'idCode') &&
69+ checkShipField(obj, 'planets') &&
70+ checkShipField(obj, 'stars') &&
71+ checkShipField(obj, 'galaxies');
6872
6973 const didPass = meetsRequirements(data) ? true : false;
7074
71− const err = didPass === false
72− ? 'File is missing data, or the data is of incorrect data type.'
73− : '';
75+ const err =
76+ didPass === false
77+ ? 'File is missing data, or the data is of incorrect data type.'
78+ : '';
7479
75− return { data: data, pass: didPass, error: err }
76−}
80+ return { data: data, pass: didPass, error: err };
81+};
7782
7883// const preview = ship => {
7984// const name = ob.patp(ship)
@@ -97,12 +102,12 @@ const validateObject = ({ data, pass, error }) => {
97102// </div>
98103// </div>
99104// </div>
100− // <div className={'flex h-16 w-60 bg-white black items-center'} key={ ship }>
101− // { sig }
102− // <div className={'pl-4 text-mono text-500'}>
103− // { name }
104− // </div>
105− // </div>
105+// <div className={'flex h-16 w-60 bg-white black items-center'} key={ ship }>
106+// { sig }
107+// <div className={'pl-4 text-mono text-500'}>
108+// { name }
109+// </div>
110+// </div>
106111// )
107112// }
108113
@@ -121,44 +126,42 @@ const validateObject = ({ data, pass, error }) => {
121126
122127class Upload extends Component {
123128 constructor(props) {
124− super(props)
125− this.handleTXTUpload = this.handleTXTUpload.bind(this)
129+ super(props);
130+ this.handleTXTUpload = this.handleTXTUpload.bind(this);
126131 }
127132
133+ handleTXTUpload = event => {
134+ const { setGlobalState } = this.props;
135+ const file = event.files.item(0);
136+ const reader = new FileReader();
128137
129− handleTXTUpload = (event) => {
130− const { setGlobalState } = this.props
131− const file = event.files.item(0)
132− const reader = new FileReader()
133−
134− reader.onload = (event) => {
135−
138+ reader.onload = event => {
136139 const validated = compose(
137140 validateObject,
138141 validateJSON,
139142 validateBase64,
140143 validateFile
141− )({ data: { event: event, file: file }, pass: true, error: '' })
144+ )({ data: { event: event, file: file }, pass: true, error: '' });
142145
143146 if (validated.pass === true) {
144− const { galaxies, stars, planets } = validated.data
147+ const { galaxies, stars, planets } = validated.data;
145148 setGlobalState({
146149 'profile.error': validated.error,
147150 'profile.state': PROFILE_STATES.UPLOAD_SUCCESS,
148151 'profile.value': validated.data,
149− 'profile.shipCount': galaxies.length + stars.length + planets.length
150− })
152+ 'profile.shipCount': galaxies.length + stars.length + planets.length,
153+ });
151154 } else {
152155 setGlobalState({
153156 'profile.error': validated.error,
154157 'profile.state': PROFILE_STATES.UPLOAD_ERROR,
155158 'profile.value': null,
156− })
159+ });
157160 }
158− }
161+ };
159162
160− reader.readAsText(file)
161− }
163+ reader.readAsText(file);
164+ };
162165
163166 render() {
164167 const { setGlobalState } = this.props;
@@ -167,72 +170,90 @@ class Upload extends Component {
167170
168171 const success =
169172 props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS ||
170− props['profile.state'] === PROFILE_STATES.INPUT_SUCCESS
173+ props['profile.state'] === PROFILE_STATES.INPUT_SUCCESS;
171174
172− let uploadButtonClass = props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
173− ? 'btn shape-orange'
174− : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
175+ let uploadButtonClass =
176+ props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
177+ ? 'btn shape-orange'
178+ : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
175179 ? 'btn btn-success'
176− : 'btn btn-primary'
180+ : 'btn btn-primary';
177181
178− let uploadButtonText = props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
179− ? 'Choose file again'
180− : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
182+ let uploadButtonText =
183+ props['profile.state'] === PROFILE_STATES.UPLOAD_ERROR
184+ ? 'Choose file again'
185+ : props['profile.state'] === PROFILE_STATES.UPLOAD_SUCCESS
181186 ? 'urbit-ships.txt'
182− : 'Choose file'
187+ : 'Choose file';
183188
184− return(
189+ return (
185190 <div className={'col-md-6'}>
186− <h2>{'Upload your '}<code>{'urbit-ships.txt'}</code></h2>
191+ <h2>
192+ {'Upload your '}
193+ <code>{'urbit-ships.txt'}</code>
194+ </h2>
187195
188− <p>{'This file contains your email and tells us what Urbit address space you own. Don’t have it? Return to Registration to download it.'}</p>
196+ <p>
197+ {
198+ 'This file contains your email and tells us what Urbit address space you own. Don’t have it? Return to Registration to download it.'
199+ }
200+ </p>
189201
190202 <UploadButton
191− className={ `${uploadButtonClass} table mt-4` }
192− onChange={ this.handleTXTUpload }
193− accept={'.txt'}
194− >
203+ className={`${uploadButtonClass} table mt-4`}
204+ onChange={this.handleTXTUpload}
205+ accept={'.txt'}>
195206 {uploadButtonText}
196207 </UploadButton>
197208
198− { props['profil.error'] !== ''
199− ? <p className={'orange'}>{props['profile.error']}</p>
200− : <div />
209+ {props['profil.error'] !== '' ? (
210+ <p className={'orange'}>{props['profile.error']}</p>
211+ ) : (
212+ <div />
213+ )}
214+ {
215+ // { success
216+ // ? <ShipList ships={props['profile.value'].galaxies} title={'Galaxies'} />
217+ // : <div />
218+ // }
219+ //
220+ // { success
221+ // ? <ShipList ships={props['profile.value'].stars} title={'Stars'} />
222+ // : <div />
223+ // }
224+ //
225+ // { success
226+ // ? <ShipList ships={props['profile.value'].planet} title={'Planets'} />
227+ // : <div />
228+ // }
201229 }
202−{
203− // { success
204− // ? <ShipList ships={props['profile.value'].galaxies} title={'Galaxies'} />
205− // : <div />
206− // }
207− //
208− // { success
209− // ? <ShipList ships={props['profile.value'].stars} title={'Stars'} />
210− // : <div />
211− // }
212− //
213− // { success
214− // ? <ShipList ships={props['profile.value'].planet} title={'Planets'} />
215− // : <div />
216− // }
217−}
218230
219231 <div className={'btn-tray'}>
220− { success
221− ? <div>
232+ {success ? (
233+ <div>
222234 {
223235 // <p>Missing ships? Email <a href={'mailto:registration@urbit.org'}>{'registration@urbit.org'}</a></p>
224236 }
225237 <Button
226− className={ success ? 'btn btn-primary mt-8' : 'btn shape-gray-10'}
227− disabled={ !success }
238+ className={
239+ success ? 'btn btn-primary mt-8' : 'btn shape-gray-10'
240+ }
241+ disabled={!success}
228242 text={"I'm ready to begin →"}
229− onClick={ () => setGlobalState({ route: '/Understand', 'currentStep': NEXT_STEP_NUM })} />
243+ onClick={() =>
244+ setGlobalState({
245+ route: '/Understand',
246+ currentStep: NEXT_STEP_NUM,
247+ })
248+ }
249+ />
230250 </div>
231− : <div />
232− }
233− </ div>
251+ ) : (
252+ <div />
253+ )}
254+ </div>
234255 </div>
235− )
256+ );
236257 }
237258}
238259
@@ -249,4 +270,4 @@ class Upload extends Component {
249270// }
250271// }
251272
252−export default Upload
273+export default Upload;
src/walletgen/views/Welcome.js+23−10
@@ -1,14 +1,14 @@
1−import React from 'react'
1+import React from 'react';
22
3−import Button from '../components/Button'
3+import Button from '../components/Button';
44
55const NEXT_STEP_NUM = 1;
66
77class Welcome extends React.Component {
88 render() {
9− let { setGlobalState } = this.props
9+ let { setGlobalState } = this.props;
1010
11− return(
11+ return (
1212 <div>
1313 <div className={'col-md-7'}>
1414 <h2>{`Welcome`}</h2>
@@ -17,21 +17,34 @@ class Welcome extends React.Component {
1717 {`If you came here from Registration and you’re ready to make a wallet for your ships. You’ve come to the right place.`}
1818 </p>
1919
20− <p>To start, you need to tell us about the ships you own by uploading the <code>urbit-ships.txt</code> you should have downloaded from the Registration website. If you didn’t get this file, please return to Registration and download it.</p>
21− <p>Reminder: Run this software offline and disable any browser extensions. If you highly value your ships, please use a new, airgapped computer.</p>
20+ <p>
21+ To start, you need to tell us about the ships you own by uploading
22+ the <code>urbit-ships.txt</code> you should have downloaded from the
23+ Registration website. If you didn’t get this file, please return to
24+ Registration and download it.
25+ </p>
26+ <p>
27+ Reminder: Run this software offline and disable any browser
28+ extensions. If you highly value your ships, please use a new,
29+ airgapped computer.
30+ </p>
2231
2332 <div className={'btn-tray'}>
24− <p><strong>{`When you’re ready to begin, start by accepting our End User License Agreement.`}</strong></p>
33+ <p>
34+ <strong>{`When you’re ready to begin, start by accepting our End User License Agreement.`}</strong>
35+ </p>
2536 <Button
2637 className={'btn btn-primary mt-8'}
2738 text={'View →'}
28− onClick={ () => setGlobalState({ 'route': '/Eula', 'currentStep': NEXT_STEP_NUM })}
39+ onClick={() =>
40+ setGlobalState({ route: '/Eula', currentStep: NEXT_STEP_NUM })
41+ }
2942 />
3043 </div>
3144 </div>
3245 </div>
33− )
46+ );
3447 }
3548}
3649
37−export default Welcome
50+export default Welcome;