From dc632be4e268d73b3a6b779f3007eb5dcff4e318 Mon Sep 17 00:00:00 2001 From: Mati Dastugue Date: Mon, 21 Sep 2020 05:41:22 -0300 Subject: [PATCH 01/11] Not able to connect with Opera Touch (#1378) * Bump new onboard.js version * Fix opera touch --- package.json | 2 +- yarn.lock | 908 ++------------------------------------------------- 2 files changed, 31 insertions(+), 879 deletions(-) diff --git a/package.json b/package.json index 5058ef7b..d9a69c18 100644 --- a/package.json +++ b/package.json @@ -176,7 +176,7 @@ "async-sema": "^3.1.0", "axios": "0.20.0", "bignumber.js": "9.0.0", - "bnc-onboard": "1.12.0", + "bnc-onboard": "1.13.1", "classnames": "^2.2.6", "concurrently": "^5.3.0", "connected-react-router": "6.8.0", diff --git a/yarn.lock b/yarn.lock index 130de018..78508b1d 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1807,16 +1807,6 @@ "@ledgerhq/logs" "^5.22.0" u2f-api "0.2.7" -"@ledgerhq/hw-transport-webusb@^5.22.0": - version "5.22.0" - resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport-webusb/-/hw-transport-webusb-5.22.0.tgz#f433b4ad8175e1b47b091766b85071c35175a27d" - integrity sha512-fPj9x+Ce48g1t7vv2RvT29KonDMUPhLMPGcsX7Kr+IRsheBnaJUhgUVzKXHevaNF1xWhkJEW/Sd8Wo5wyK1pOw== - dependencies: - "@ledgerhq/devices" "^5.22.0" - "@ledgerhq/errors" "^5.22.0" - "@ledgerhq/hw-transport" "^5.22.0" - "@ledgerhq/logs" "^5.22.0" - "@ledgerhq/hw-transport@^5.22.0": version "5.22.0" resolved "https://registry.yarnpkg.com/@ledgerhq/hw-transport/-/hw-transport-5.22.0.tgz#d627948b43005ec9e7dfe85adf9aa01e130de280" @@ -3819,13 +3809,6 @@ ansi-colors@4.1.1, ansi-colors@^4.1.1: resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-4.1.1.tgz#cbb9ae256bf750af1eab344f229aa27fe94ba348" integrity sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA== -ansi-colors@^1.0.1: - version "1.1.0" - resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-1.1.0.tgz#6374b4dd5d4718ff3ce27a671a3b1cad077132a9" - integrity sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA== - dependencies: - ansi-wrap "^0.1.0" - ansi-colors@^3.0.0: version "3.2.4" resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf" @@ -3843,13 +3826,6 @@ ansi-escapes@^4.2.1, ansi-escapes@^4.3.0: dependencies: type-fest "^0.11.0" -ansi-gray@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/ansi-gray/-/ansi-gray-0.1.1.tgz#2962cf54ec9792c48510a3deb524436861ef7251" - integrity sha1-KWLPVOyXksSFEKPetSRDaGHvclE= - dependencies: - ansi-wrap "0.1.0" - ansi-html@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/ansi-html/-/ansi-html-0.0.7.tgz#813584021962a9e9e6fd039f940d12f56ca7859e" @@ -3902,11 +3878,6 @@ ansi-to-html@^0.6.11: dependencies: entities "^1.1.2" -ansi-wrap@0.1.0, ansi-wrap@^0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/ansi-wrap/-/ansi-wrap-0.1.0.tgz#a82250ddb0015e9a27ca82e82ea603bbfa45efaf" - integrity sha1-qCJQ3bABXponyoLoLqYDu/pF768= - any-promise@1.3.0, any-promise@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/any-promise/-/any-promise-1.3.0.tgz#abc6afeedcea52e809cdc0376aed3ce39635d17f" @@ -3971,23 +3942,11 @@ app-root-dir@^1.0.2: resolved "https://registry.yarnpkg.com/app-root-dir/-/app-root-dir-1.0.2.tgz#38187ec2dea7577fff033ffcb12172692ff6e118" integrity sha1-OBh+wt6nV3//Az/8sSFyaS/24Rg= -append-buffer@^1.0.2: - version "1.0.2" - resolved "https://registry.yarnpkg.com/append-buffer/-/append-buffer-1.0.2.tgz#d8220cf466081525efea50614f3de6514dfa58f1" - integrity sha1-2CIM9GYIFSXv6lBhTz3mUU36WPE= - dependencies: - buffer-equal "^1.0.0" - aproba@^1.0.3, aproba@^1.1.1: version "1.2.0" resolved "https://registry.yarnpkg.com/aproba/-/aproba-1.2.0.tgz#6802e6264efd18c790a1b0d517f0f2627bf2c94a" integrity sha512-Y9J6ZjXtoYh8RnXVCMOU/ttDmk1aBjunq9vO0ta5x85WDQiQfUF9sIPBITdbiiIVcBo03Hi3jMxigBtsddlXRw== -archy@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/archy/-/archy-1.0.0.tgz#f9c8c13757cc1dd7bc379ac77b2c62a5c2868c40" - integrity sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= - are-we-there-yet@~1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/are-we-there-yet/-/are-we-there-yet-1.1.5.tgz#4b35c2944f062a8bfcda66410760350fe9ddfc21" @@ -4029,25 +3988,11 @@ arr-diff@^4.0.0: resolved "https://registry.yarnpkg.com/arr-diff/-/arr-diff-4.0.0.tgz#d6461074febfec71e7e15235761a329a5dc7c520" integrity sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= -arr-filter@^1.1.1: - version "1.1.2" - resolved "https://registry.yarnpkg.com/arr-filter/-/arr-filter-1.1.2.tgz#43fdddd091e8ef11aa4c45d9cdc18e2dff1711ee" - integrity sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= - dependencies: - make-iterator "^1.0.0" - -arr-flatten@^1.0.1, arr-flatten@^1.1.0: +arr-flatten@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/arr-flatten/-/arr-flatten-1.1.0.tgz#36048bbff4e7b47e136644316c99669ea5ae91f1" integrity sha512-L3hKV5R/p5o81R7O02IGnwpDmkp6E982XhtbuwSe3O4qOtMMMtodicASA1Cny2U+aCXcNpml+m4dPsvsJ3jatg== -arr-map@^2.0.0, arr-map@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/arr-map/-/arr-map-2.0.2.tgz#3a77345ffc1cf35e2a91825601f9e58f2e24cac4" - integrity sha1-Onc0X/wc814qkYJWAfnljy4kysQ= - dependencies: - make-iterator "^1.0.0" - arr-union@^3.1.0: version "3.1.0" resolved "https://registry.yarnpkg.com/arr-union/-/arr-union-3.1.0.tgz#e39b09aea9def866a8f206e288af63919bae39c4" @@ -4067,11 +4012,6 @@ array-back@^2.0.0: dependencies: typical "^2.6.1" -array-each@^1.0.0, array-each@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/array-each/-/array-each-1.0.1.tgz#a794af0c05ab1752846ee753a1f211a05ba0c44f" - integrity sha1-p5SvDAWrF1KEbudTofIRoFugxE8= - array-equal@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-equal/-/array-equal-1.0.0.tgz#8c2a5ef2472fd9ea742b04c77a75093ba2757c93" @@ -4101,35 +4041,6 @@ array-includes@^3.0.3, array-includes@^3.1.1: es-abstract "^1.17.0" is-string "^1.0.5" -array-initial@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-initial/-/array-initial-1.1.0.tgz#2fa74b26739371c3947bd7a7adc73be334b3d795" - integrity sha1-L6dLJnOTccOUe9enrcc74zSz15U= - dependencies: - array-slice "^1.0.0" - is-number "^4.0.0" - -array-last@^1.1.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/array-last/-/array-last-1.3.0.tgz#7aa77073fec565ddab2493f5f88185f404a9d336" - integrity sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg== - dependencies: - is-number "^4.0.0" - -array-slice@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/array-slice/-/array-slice-1.1.0.tgz#e368ea15f89bc7069f7ffb89aec3a6c7d4ac22d4" - integrity sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w== - -array-sort@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/array-sort/-/array-sort-1.0.0.tgz#e4c05356453f56f53512a7d1d6123f2c54c0a88a" - integrity sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg== - dependencies: - default-compare "^1.0.0" - get-value "^2.0.6" - kind-of "^5.0.2" - array-union@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/array-union/-/array-union-1.0.2.tgz#9a34410e4f4e3da23dea375be5be70f24778ec39" @@ -4251,16 +4162,6 @@ astral-regex@^2.0.0: resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31" integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ== -async-done@^1.2.0, async-done@^1.2.2: - version "1.3.2" - resolved "https://registry.yarnpkg.com/async-done/-/async-done-1.3.2.tgz#5e15aa729962a4b07414f528a88cdf18e0b290a2" - integrity sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw== - dependencies: - end-of-stream "^1.1.0" - once "^1.3.2" - process-nextick-args "^2.0.0" - stream-exhaust "^1.0.1" - async-each@^1.0.1: version "1.0.3" resolved "https://registry.yarnpkg.com/async-each/-/async-each-1.0.3.tgz#b727dbf87d7651602f06f4d4ac387f47d91b0cbf" @@ -4293,13 +4194,6 @@ async-sema@^3.1.0: resolved "https://registry.yarnpkg.com/async-sema/-/async-sema-3.1.0.tgz#3a813beb261e4cc58b19213916a48e931e21d21e" integrity sha512-+JpRq3r0zjpRLDruS6q/nC4V5tzsaiu07521677Mdi5i+AkaU/aNJH38rYHJVQ4zvz+SSkjgc8FUI7qIZrR+3g== -async-settle@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/async-settle/-/async-settle-1.0.0.tgz#1d0a914bb02575bec8a8f3a74e5080f72b2c0c6b" - integrity sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= - dependencies: - async-done "^1.2.2" - async@0.9.x: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -5309,21 +5203,6 @@ babylon@^6.18.0: resolved "https://registry.yarnpkg.com/babylon/-/babylon-6.18.0.tgz#af2f3b88fa6f5c1e4c634d1a0f8eac4f55b395e3" integrity sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ== -bach@^1.0.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/bach/-/bach-1.2.0.tgz#4b3ce96bf27134f79a1b414a51c14e34c3bd9880" - integrity sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= - dependencies: - arr-filter "^1.1.1" - arr-flatten "^1.0.1" - arr-map "^2.0.0" - array-each "^1.0.0" - array-initial "^1.0.0" - array-last "^1.1.1" - async-done "^1.2.2" - async-settle "^1.0.0" - now-and-later "^2.0.0" - backoff@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/backoff/-/backoff-2.5.0.tgz#f616eda9d3e4b66b8ca7fca79f695722c5f8e26f" @@ -5485,14 +5364,13 @@ bn.js@^5.1.1, bn.js@^5.1.2: resolved "https://registry.yarnpkg.com/bn.js/-/bn.js-5.1.3.tgz#beca005408f642ebebea80b042b4d18d2ac0ee6b" integrity sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ== -bnc-onboard@1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/bnc-onboard/-/bnc-onboard-1.12.0.tgz#b6c447ae6c98bbaf3f2dcd1e41b8a9ede22445a5" - integrity sha512-vWNlmOMFaWFZ8JyhHnbNOOaU2cuwNPUdAIz6CsyzYfcOTH/6bEJSk6r6VhWtl3B9NEud+MFjBd2WBpzF7Mrg4w== +bnc-onboard@1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/bnc-onboard/-/bnc-onboard-1.13.1.tgz#281629e15c01ab47425f9494b33c4ce28a9db01e" + integrity sha512-Mv06oWNjkjDNU3vR8l/aJFKTBya5lj6vPfYcl9ONpyyaoY/8neZS4icQrFeRv9nDvENVz6esxcYAyX62f0+rwA== dependencies: "@ledgerhq/hw-app-eth" "^5.21.0" "@ledgerhq/hw-transport-u2f" "^5.21.0" - "@ledgerhq/hw-transport-webusb" "^5.22.0" "@portis/web3" "^2.0.0-beta.57" "@toruslabs/torus-embed" "^1.8.2" "@unilogin/provider" "^0.6.1" @@ -5781,11 +5659,6 @@ buffer-crc32@~0.2.3: resolved "https://registry.yarnpkg.com/buffer-crc32/-/buffer-crc32-0.2.13.tgz#0d333e3f00eac50aa1454abd30ef8c2a5d9a7242" integrity sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI= -buffer-equal@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/buffer-equal/-/buffer-equal-1.0.0.tgz#59616b498304d556abd466966b22eeda3eca5fbe" - integrity sha1-WWFrSYME1Var1GaWayLu2j7KX74= - buffer-fill@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/buffer-fill/-/buffer-fill-1.0.0.tgz#f8f78b76789888ef39f205cd637f68e702122b2c" @@ -6013,11 +5886,6 @@ camelcase@^2.0.0: resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" integrity sha1-fB0W1nmhu+WcoCys7PsBHiAfWh8= -camelcase@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-3.0.0.tgz#32fc4b9fcdaf845fcdf7e73bb97cac2261f0ab0a" - integrity sha1-MvxLn82vhF/N9+c7uXysImHwqwo= - camelcase@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-4.1.0.tgz#d545635be1e33c542649c69173e5de6acfae34dd" @@ -6153,7 +6021,7 @@ chokidar@3.3.1: optionalDependencies: fsevents "~2.1.2" -chokidar@^2.0.0, chokidar@^2.0.4, chokidar@^2.1.8: +chokidar@^2.0.4, chokidar@^2.1.8: version "2.1.8" resolved "https://registry.yarnpkg.com/chokidar/-/chokidar-2.1.8.tgz#804b3a7b6a99358c3c5c61e71d8728f041cff917" integrity sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg== @@ -6316,15 +6184,6 @@ clipboard@^2.0.0: select "^1.1.2" tiny-emitter "^2.0.0" -cliui@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" - integrity sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi "^2.0.0" - cliui@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-5.0.0.tgz#deefcfdb2e800784aa34f46fa08e06851c7bbbc5" @@ -6343,11 +6202,6 @@ cliui@^6.0.0: strip-ansi "^6.0.0" wrap-ansi "^6.2.0" -clone-buffer@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-buffer/-/clone-buffer-1.0.0.tgz#e3e25b207ac4e701af721e2cb5a16792cac3dc58" - integrity sha1-4+JbIHrE5wGvch4staFnksrD3Fg= - clone-deep@^0.2.4: version "0.2.4" resolved "https://registry.yarnpkg.com/clone-deep/-/clone-deep-0.2.4.tgz#4e73dd09e9fb971cc38670c5dced9c1896481cc6" @@ -6375,25 +6229,11 @@ clone-response@^1.0.2: dependencies: mimic-response "^1.0.0" -clone-stats@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/clone-stats/-/clone-stats-1.0.0.tgz#b3782dff8bb5474e18b9b6bf0fdfe782f8777680" - integrity sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= - clone@^2.0.0, clone@^2.1.1: version "2.1.2" resolved "https://registry.yarnpkg.com/clone/-/clone-2.1.2.tgz#1b7f4b9f591f1e8f83670401600345a02887435f" integrity sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18= -cloneable-readable@^1.0.0: - version "1.1.3" - resolved "https://registry.yarnpkg.com/cloneable-readable/-/cloneable-readable-1.1.3.tgz#120a00cb053bfb63a222e709f9683ea2e11d8cec" - integrity sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ== - dependencies: - inherits "^2.0.1" - process-nextick-args "^2.0.0" - readable-stream "^2.3.5" - clsx@^1.0.4, clsx@^1.1.0: version "1.1.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.1.1.tgz#98b3134f9abbdf23b2663491ace13c5c03a73188" @@ -6418,15 +6258,6 @@ code-point-at@^1.0.0: resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77" integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= -collection-map@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/collection-map/-/collection-map-1.0.0.tgz#aea0f06f8d26c780c2b75494385544b2255af18c" - integrity sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= - dependencies: - arr-map "^2.0.2" - for-own "^1.0.0" - make-iterator "^1.0.0" - collection-visit@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/collection-visit/-/collection-visit-1.0.0.tgz#4bc0373c164bc3291b4d368c829cf1a80a59dca0" @@ -6467,11 +6298,6 @@ color-string@^1.5.2: color-name "^1.0.0" simple-swizzle "^0.2.2" -color-support@^1.1.3: - version "1.1.3" - resolved "https://registry.yarnpkg.com/color-support/-/color-support-1.1.3.tgz#93834379a1cc9a0c61f82f52f0d04322251bd5a2" - integrity sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg== - color@^3.0.0: version "3.1.2" resolved "https://registry.yarnpkg.com/color/-/color-3.1.2.tgz#68148e7f85d41ad7649c5fa8c8106f098d229e10" @@ -6588,7 +6414,7 @@ concat-map@0.0.1: resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" integrity sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= -concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.6.0, concat-stream@^1.6.2: +concat-stream@^1.5.0, concat-stream@^1.5.1, concat-stream@^1.6.2: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" integrity sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw== @@ -6735,14 +6561,6 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= -copy-props@^2.0.1: - version "2.0.4" - resolved "https://registry.yarnpkg.com/copy-props/-/copy-props-2.0.4.tgz#93bb1cadfafd31da5bb8a9d4b41f471ec3a72dfe" - integrity sha512-7cjuUME+p+S3HZlbllgsn2CDwS+5eCCX16qBgNC4jgSTf49qR1VKy/Zhl400m0IQXl/bPGEVqncgUUMjrr4s8A== - dependencies: - each-props "^1.3.0" - is-plain-object "^2.0.1" - copy-to-clipboard@^3.0.8: version "3.3.1" resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.1.tgz#115aa1a9998ffab6196f93076ad6da3b913662ae" @@ -7333,7 +7151,7 @@ decamelize-keys@^1.0.0: decamelize "^1.1.0" map-obj "^1.0.0" -decamelize@^1.1.0, decamelize@^1.1.1, decamelize@^1.1.2, decamelize@^1.2.0: +decamelize@^1.1.0, decamelize@^1.1.2, decamelize@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/decamelize/-/decamelize-1.2.0.tgz#f6534d15148269b20352e7bee26f501f9a191290" integrity sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= @@ -7447,13 +7265,6 @@ deepmerge@^4.2.2: resolved "https://registry.yarnpkg.com/deepmerge/-/deepmerge-4.2.2.tgz#44d2ea3679b8f4d4ffba33f03d865fc1e7bf4955" integrity sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg== -default-compare@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/default-compare/-/default-compare-1.0.0.tgz#cb61131844ad84d84788fb68fd01681ca7781a2f" - integrity sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ== - dependencies: - kind-of "^5.0.2" - default-gateway@^4.2.0: version "4.2.0" resolved "https://registry.yarnpkg.com/default-gateway/-/default-gateway-4.2.0.tgz#167104c7500c2115f6dd69b0a536bb8ed720552b" @@ -7462,11 +7273,6 @@ default-gateway@^4.2.0: execa "^1.0.0" ip-regex "^2.1.0" -default-resolution@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/default-resolution/-/default-resolution-2.0.0.tgz#bcb82baa72ad79b426a76732f1a81ad6df26d684" - integrity sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= - defer-to-connect@^1.0.1: version "1.1.3" resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-1.1.3.tgz#331ae050c08dcf789f8c83a7b81f0ed94f4ac591" @@ -7564,11 +7370,6 @@ detect-browser@5.1.0: resolved "https://registry.yarnpkg.com/detect-browser/-/detect-browser-5.1.0.tgz#0c51c66b747ad8f98a6832bf3026a5a23a7850ff" integrity sha512-WKa9p+/MNwmTiS+V2AS6eGxic+807qvnV3hC+4z2GTY+F42h1n8AynVTMMc4EJBC32qMs6yjOTpeDEQQt/AVqQ== -detect-file@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/detect-file/-/detect-file-1.0.0.tgz#f0d66d03672a825cb1b73bdb3fe62310c8e552b7" - integrity sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= - detect-indent@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/detect-indent/-/detect-indent-4.0.0.tgz#f76d064352cdf43a1cb6ce619c4ee3a9475de208" @@ -7861,14 +7662,6 @@ duplexify@^3.4.2, duplexify@^3.6.0: readable-stream "^2.0.0" stream-shift "^1.0.0" -each-props@^1.3.0: - version "1.3.2" - resolved "https://registry.yarnpkg.com/each-props/-/each-props-1.3.2.tgz#ea45a414d16dd5cfa419b1a81720d5ca06892333" - integrity sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA== - dependencies: - is-plain-object "^2.0.1" - object.defaults "^1.1.0" - ecc-jsbn@~0.1.1: version "0.1.2" resolved "https://registry.yarnpkg.com/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz#3a83a904e54353287874c564b7549386849a98c9" @@ -8223,7 +8016,7 @@ es-to-primitive@^1.2.1: is-date-object "^1.0.1" is-symbol "^1.0.2" -es5-ext@^0.10.35, es5-ext@^0.10.46, es5-ext@^0.10.50: +es5-ext@^0.10.35, es5-ext@^0.10.50: version "0.10.53" resolved "https://registry.yarnpkg.com/es5-ext/-/es5-ext-0.10.53.tgz#93c5a3acfdbef275220ad72644ad02ee18368de1" integrity sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q== @@ -8242,7 +8035,7 @@ es6-error@^4.1.1: resolved "https://registry.yarnpkg.com/es6-error/-/es6-error-4.1.1.tgz#9e3af407459deed47e9a91f9b885a84eb05c561d" integrity sha512-Um/+FxMr9CISWh0bi5Zv0iOD+4cFh5qLeks1qhAopKVAJw3drgKbKySikp7wGhDL0HPeaja0P5ULZrxLkniUVg== -es6-iterator@2.0.3, es6-iterator@^2.0.1, es6-iterator@^2.0.3, es6-iterator@~2.0.3: +es6-iterator@2.0.3, es6-iterator@~2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/es6-iterator/-/es6-iterator-2.0.3.tgz#a7de889141a05a94b0854403b2d0a0fbfa98f3b7" integrity sha1-p96IkUGgWpSwhUQDstCg+/qY87c= @@ -8269,16 +8062,6 @@ es6-symbol@^3.1.1, es6-symbol@~3.1.3: d "^1.0.1" ext "^1.1.2" -es6-weak-map@^2.0.1: - version "2.0.3" - resolved "https://registry.yarnpkg.com/es6-weak-map/-/es6-weak-map-2.0.3.tgz#b6da1f16cc2cc0d9be43e6bdbfc5e7dfcdf31d53" - integrity sha512-p5um32HOTO1kP+w7PRnB+5lQ43Z6muuMuIMffvDN8ZB4GcnjLBV6zGStpbASIMk4DCAvEaamhe2zhyCb/QXXsA== - dependencies: - d "1" - es5-ext "^0.10.46" - es6-iterator "^2.0.3" - es6-symbol "^3.1.1" - escalade@^3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/escalade/-/escalade-3.0.2.tgz#6a580d70edb87880f22b4c91d0d56078df6962c4" @@ -9244,13 +9027,6 @@ expand-template@^2.0.3: resolved "https://registry.yarnpkg.com/expand-template/-/expand-template-2.0.3.tgz#6e14b3fcee0f3a6340ecb57d2e8918692052a47c" integrity sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg== -expand-tilde@^2.0.0, expand-tilde@^2.0.2: - version "2.0.2" - resolved "https://registry.yarnpkg.com/expand-tilde/-/expand-tilde-2.0.2.tgz#97e801aa052df02454de46b02bf621642cdc8502" - integrity sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= - dependencies: - homedir-polyfill "^1.0.1" - expect@^24.9.0: version "24.9.0" resolved "https://registry.yarnpkg.com/expect/-/expect-24.9.0.tgz#b75165b4817074fa4a157794f46fe9f1ba15b6ca" @@ -9326,7 +9102,7 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: assign-symbols "^1.0.0" is-extendable "^1.0.1" -extend@^3.0.0, extend@~3.0.2: +extend@~3.0.2: version "3.0.2" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== @@ -9381,16 +9157,6 @@ fake-merkle-patricia-tree@^1.0.1: dependencies: checkpoint-store "^1.1.0" -fancy-log@^1.3.2: - version "1.3.3" - resolved "https://registry.yarnpkg.com/fancy-log/-/fancy-log-1.3.3.tgz#dbc19154f558690150a23953a0adbd035be45fc7" - integrity sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw== - dependencies: - ansi-gray "^0.1.1" - color-support "^1.1.3" - parse-node-version "^1.0.0" - time-stamp "^1.0.0" - fast-deep-equal@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/fast-deep-equal/-/fast-deep-equal-2.0.1.tgz#7b05218ddf9667bf7f370bf7fdb2cb15fdd0aa49" @@ -9428,11 +9194,6 @@ fast-json-stable-stringify@^2.0.0: resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz#874bf69c6f404c2b5d99c481341399fd55892633" integrity sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw== -fast-levenshtein@^1.0.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz#e6a754cc8f15e58987aa9cbd27af66fd6f4e5af9" - integrity sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= - fast-levenshtein@^2.0.6, fast-levenshtein@~2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz#3d8a5c66883a16a30ca8643e851f19baa7797917" @@ -9697,42 +9458,6 @@ find-versions@^3.2.0: dependencies: semver-regex "^2.0.0" -findup-sync@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-2.0.0.tgz#9326b1488c22d1a6088650a86901b2d9a90a2cbc" - integrity sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= - dependencies: - detect-file "^1.0.0" - is-glob "^3.1.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -findup-sync@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/findup-sync/-/findup-sync-3.0.0.tgz#17b108f9ee512dfb7a5c7f3c8b27ea9e1a9c08d1" - integrity sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg== - dependencies: - detect-file "^1.0.0" - is-glob "^4.0.0" - micromatch "^3.0.4" - resolve-dir "^1.0.1" - -fined@^1.0.1: - version "1.2.0" - resolved "https://registry.yarnpkg.com/fined/-/fined-1.2.0.tgz#d00beccf1aa2b475d16d423b0238b713a2c4a37b" - integrity sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng== - dependencies: - expand-tilde "^2.0.2" - is-plain-object "^2.0.3" - object.defaults "^1.1.0" - object.pick "^1.2.0" - parse-filepath "^1.0.1" - -flagged-respawn@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/flagged-respawn/-/flagged-respawn-1.0.1.tgz#e7de6f1279ddd9ca9aac8a5971d618606b3aab41" - integrity sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q== - flat-cache@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/flat-cache/-/flat-cache-2.0.1.tgz#5d296d6f04bda44a4630a301413bdbc2ec085ec0" @@ -9759,7 +9484,7 @@ flatten@^1.0.2: resolved "https://registry.yarnpkg.com/flatten/-/flatten-1.0.3.tgz#c1283ac9f27b368abc1e36d1ff7b04501a30356b" integrity sha512-dVsPA/UwQ8+2uoFe5GHtiBMu48dWLTdsuEd7CKGlZlD78r1TTWBvDuFaFGKCo/ZfEr95Uk56vZoX86OsHkUeIg== -flush-write-stream@^1.0.0, flush-write-stream@^1.0.2: +flush-write-stream@^1.0.0: version "1.1.1" resolved "https://registry.yarnpkg.com/flush-write-stream/-/flush-write-stream-1.1.1.tgz#8dd7d873a1babc207d94ead0c2e0e44276ebf2e8" integrity sha512-3Z4XhFZ3992uIq0XOqb9AreonueSYphE6oYbpt5+3u06JWklbsPkNv3ZKkP9Bz/r+1MWCaMoSQ28P85+1Yc77w== @@ -9808,13 +9533,6 @@ for-own@^0.1.3: dependencies: for-in "^1.0.1" -for-own@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/for-own/-/for-own-1.0.0.tgz#c63332f415cedc4b04dbfe70cf836494c53cb44b" - integrity sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= - dependencies: - for-in "^1.0.1" - forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" @@ -9959,14 +9677,6 @@ fs-minipass@^2.0.0: dependencies: minipass "^3.0.0" -fs-mkdirp-stream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz#0b7815fc3201c6a69e14db98ce098c16935259eb" - integrity sha1-C3gV/DIBxqaeFNuYzgmMFpNSWes= - dependencies: - graceful-fs "^4.1.11" - through2 "^2.0.3" - fs-write-stream-atomic@^1.0.8: version "1.0.10" resolved "https://registry.yarnpkg.com/fs-write-stream-atomic/-/fs-write-stream-atomic-1.0.10.tgz#b47df53493ef911df75731e70a9ded0189db40c9" @@ -10055,11 +9765,6 @@ gensync@^1.0.0-beta.1: resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269" integrity sha512-r8EC6NO1sngH/zdD9fiRDLdcgnbayXah+mLgManTaIZJqEC1MZstmnox8KpnI2/fxQwrp5OpCOYWLp4rBl4Jcg== -get-caller-file@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-1.0.3.tgz#f978fa4c90d1dfe7ff2d6beda2a515e713bdcf4a" - integrity sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w== - get-caller-file@^2.0.1: version "2.0.5" resolved "https://registry.yarnpkg.com/get-caller-file/-/get-caller-file-2.0.5.tgz#4f94412a82db32f36e3b0b9741f8a97feb031f7e" @@ -10154,40 +9859,11 @@ glob-parent@^5.0.0, glob-parent@~5.1.0: dependencies: is-glob "^4.0.1" -glob-stream@^6.1.0: - version "6.1.0" - resolved "https://registry.yarnpkg.com/glob-stream/-/glob-stream-6.1.0.tgz#7045c99413b3eb94888d83ab46d0b404cc7bdde4" - integrity sha1-cEXJlBOz65SIjYOrRtC0BMx73eQ= - dependencies: - extend "^3.0.0" - glob "^7.1.1" - glob-parent "^3.1.0" - is-negated-glob "^1.0.0" - ordered-read-streams "^1.0.0" - pumpify "^1.3.5" - readable-stream "^2.1.5" - remove-trailing-separator "^1.0.1" - to-absolute-glob "^2.0.0" - unique-stream "^2.0.2" - glob-to-regexp@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/glob-to-regexp/-/glob-to-regexp-0.3.0.tgz#8c5a1494d2066c570cc3bfe4496175acc4d502ab" integrity sha1-jFoUlNIGbFcMw7/kSWF1rMTVAqs= -glob-watcher@^5.0.3: - version "5.0.5" - resolved "https://registry.yarnpkg.com/glob-watcher/-/glob-watcher-5.0.5.tgz#aa6bce648332924d9a8489be41e3e5c52d4186dc" - integrity sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw== - dependencies: - anymatch "^2.0.0" - async-done "^1.2.0" - chokidar "^2.0.0" - is-negated-glob "^1.0.0" - just-debounce "^1.0.0" - normalize-path "^3.0.0" - object.defaults "^1.1.0" - glob@7.1.6, glob@^7.0.0, glob@^7.0.3, glob@^7.1.1, glob@^7.1.2, glob@^7.1.3, glob@^7.1.4, glob@^7.1.6, glob@~7.1.1, glob@~7.1.6: version "7.1.6" resolved "https://registry.yarnpkg.com/glob/-/glob-7.1.6.tgz#141f33b81a7c2492e125594307480c46679278a6" @@ -10227,26 +9903,6 @@ global-modules@2.0.0: dependencies: global-prefix "^3.0.0" -global-modules@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/global-modules/-/global-modules-1.0.0.tgz#6d770f0eb523ac78164d72b5e71a8877265cc3ea" - integrity sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg== - dependencies: - global-prefix "^1.0.1" - is-windows "^1.0.1" - resolve-dir "^1.0.0" - -global-prefix@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-1.0.2.tgz#dbf743c6c14992593c655568cb66ed32c0122ebe" - integrity sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= - dependencies: - expand-tilde "^2.0.2" - homedir-polyfill "^1.0.1" - ini "^1.3.4" - is-windows "^1.0.1" - which "^1.2.14" - global-prefix@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/global-prefix/-/global-prefix-3.0.0.tgz#fc85f73064df69f50421f47f883fe5b913ba9b97" @@ -10339,13 +9995,6 @@ globule@^1.0.0: lodash "~4.17.10" minimatch "~3.0.2" -glogg@^1.0.0: - version "1.0.2" - resolved "https://registry.yarnpkg.com/glogg/-/glogg-1.0.2.tgz#2d7dd702beda22eb3bffadf880696da6d846313f" - integrity sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA== - dependencies: - sparkles "^1.0.0" - good-listener@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" @@ -10390,7 +10039,7 @@ got@^7.1.0: url-parse-lax "^1.0.0" url-to-options "^1.0.1" -graceful-fs@^4.0.0, graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2: +graceful-fs@^4.1.10, graceful-fs@^4.1.11, graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.1.9, graceful-fs@^4.2.0, graceful-fs@^4.2.2: version "4.2.4" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb" integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw== @@ -10410,47 +10059,6 @@ gud@^1.0.0: resolved "https://registry.yarnpkg.com/gud/-/gud-1.0.0.tgz#a489581b17e6a70beca9abe3ae57de7a499852c0" integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== -gulp-cli@^2.2.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/gulp-cli/-/gulp-cli-2.3.0.tgz#ec0d380e29e52aa45e47977f0d32e18fd161122f" - integrity sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A== - dependencies: - ansi-colors "^1.0.1" - archy "^1.0.0" - array-sort "^1.0.0" - color-support "^1.1.3" - concat-stream "^1.6.0" - copy-props "^2.0.1" - fancy-log "^1.3.2" - gulplog "^1.0.0" - interpret "^1.4.0" - isobject "^3.0.1" - liftoff "^3.1.0" - matchdep "^2.0.0" - mute-stdout "^1.0.0" - pretty-hrtime "^1.0.0" - replace-homedir "^1.0.0" - semver-greatest-satisfied-range "^1.1.0" - v8flags "^3.2.0" - yargs "^7.1.0" - -gulp@^4.0.2: - version "4.0.2" - resolved "https://registry.yarnpkg.com/gulp/-/gulp-4.0.2.tgz#543651070fd0f6ab0a0650c6a3e6ff5a7cb09caa" - integrity sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA== - dependencies: - glob-watcher "^5.0.3" - gulp-cli "^2.2.0" - undertaker "^1.2.1" - vinyl-fs "^3.0.0" - -gulplog@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/gulplog/-/gulplog-1.0.0.tgz#e28c4d45d05ecbbed818363ce8f9c5926229ffe5" - integrity sha1-4oxNRdBey77YGDY86PnFkmIp/+U= - dependencies: - glogg "^1.0.0" - gzip-size@5.1.1: version "5.1.1" resolved "https://registry.yarnpkg.com/gzip-size/-/gzip-size-5.1.1.tgz#cb9bee692f87c0612b232840a873904e4c135274" @@ -10669,13 +10277,6 @@ home-or-tmp@^2.0.0: os-homedir "^1.0.0" os-tmpdir "^1.0.1" -homedir-polyfill@^1.0.1: - version "1.0.3" - resolved "https://registry.yarnpkg.com/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz#743298cef4e5af3e194161fbadcc2151d3a058e8" - integrity sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA== - dependencies: - parse-passwd "^1.0.0" - hosted-git-info@^2.1.4: version "2.8.8" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.8.tgz#7539bd4bc1e0e0a895815a2e0262420b12858488" @@ -11158,7 +10759,7 @@ internal-slot@^1.0.2: has "^1.0.3" side-channel "^1.0.2" -interpret@^1.0.0, interpret@^1.4.0: +interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" integrity sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA== @@ -11175,11 +10776,6 @@ invariant@^2.2.2, invariant@^2.2.3, invariant@^2.2.4: dependencies: loose-envify "^1.0.0" -invert-kv@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/invert-kv/-/invert-kv-1.0.0.tgz#104a8e4aaca6d3d8cd157a8ef8bfab2d7a3ffdb6" - integrity sha1-EEqOSqym09jNFXqO+L+rLXo//bY= - ip-regex@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/ip-regex/-/ip-regex-2.1.0.tgz#fa78bf5d2e6913c911ce9f819ee5146bb6d844e9" @@ -11205,14 +10801,6 @@ is-absolute-url@^3.0.3: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.3.tgz#96c6a22b6a23929b11ea0afb1836c36ad4a5d698" integrity sha512-opmNIX7uFnS96NtPmhWQgQx6/NYFgsUXYMllcfzwWKUMwfo8kku1TvE6hkNcH+Q1ts5cMVrsY7j0bxXQDciu9Q== -is-absolute@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-absolute/-/is-absolute-1.0.0.tgz#395e1ae84b11f26ad1795e73c17378e48a301576" - integrity sha512-dOWoqflvcydARa360Gvv18DZ/gRuHKi2NU/wU5X1ZFzdYfH29nkiNZsF3mp4OJ3H4yo9Mx8A/uAGNzpzPN3yBA== - dependencies: - is-relative "^1.0.0" - is-windows "^1.0.1" - is-accessor-descriptor@^0.1.6: version "0.1.6" resolved "https://registry.yarnpkg.com/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz#a9e12cb3ae8d876727eeef3843f8a0897b5c98d6" @@ -11476,11 +11064,6 @@ is-natural-number@^4.0.1: resolved "https://registry.yarnpkg.com/is-natural-number/-/is-natural-number-4.0.1.tgz#ab9d76e1db4ced51e35de0c72ebecf09f734cde8" integrity sha1-q5124dtM7VHjXeDHLr7PCfc0zeg= -is-negated-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-negated-glob/-/is-negated-glob-1.0.0.tgz#6910bca5da8c95e784b5751b976cf5a10fee36d2" - integrity sha1-aRC8pdqMleeEtXUbl2z1oQ/uNtI= - is-negative-zero@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.0.tgz#9553b121b0fac28869da9ed459e20c7543788461" @@ -11498,11 +11081,6 @@ is-number@^3.0.0: dependencies: kind-of "^3.0.2" -is-number@^4.0.0: - version "4.0.0" - resolved "https://registry.yarnpkg.com/is-number/-/is-number-4.0.0.tgz#0026e37f5454d73e356dfe6564699867c6a7f0ff" - integrity sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ== - is-number@^7.0.0: version "7.0.0" resolved "https://registry.yarnpkg.com/is-number/-/is-number-7.0.0.tgz#7535345b896734d5f80c4d06c50955527a14f12b" @@ -11583,13 +11161,6 @@ is-regexp@^1.0.0: resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069" integrity sha1-/S2INUXEa6xaYz57mgnof6LLUGk= -is-relative@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-relative/-/is-relative-1.0.0.tgz#a1bb6935ce8c5dba1e8b9754b9b2dcc020e2260d" - integrity sha512-Kw/ReK0iqwKeu0MITLFuj0jbPAmEiOsIwyIXvvbfa6QfmN9pkD1M+8pdk7Rl/dTKbH34/XBFMbgD4iMJhLQbGA== - dependencies: - is-unc-path "^1.0.0" - is-resolvable@^1.0.0: version "1.1.0" resolved "https://registry.yarnpkg.com/is-resolvable/-/is-resolvable-1.1.0.tgz#fb18f87ce1feb925169c9a407c19318a3206ed88" @@ -11644,29 +11215,17 @@ is-typedarray@1.0.0, is-typedarray@^1.0.0, is-typedarray@~1.0.0: resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" integrity sha1-5HnICFjfDBsR3dppQPlgEfzaSpo= -is-unc-path@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-unc-path/-/is-unc-path-1.0.0.tgz#d731e8898ed090a12c352ad2eaed5095ad322c9d" - integrity sha512-mrGpVd0fs7WWLfVsStvgF6iEJnbjDFZh9/emhRDcGWTduTfNHd9CHeUwH3gYIjdbwo4On6hunkztwOaAw0yllQ== - dependencies: - unc-path-regex "^0.1.2" - -is-utf8@^0.2.0, is-utf8@^0.2.1: +is-utf8@^0.2.0: version "0.2.1" resolved "https://registry.yarnpkg.com/is-utf8/-/is-utf8-0.2.1.tgz#4b0da1442104d1b336340e80797e865cf39f7d72" integrity sha1-Sw2hRCEE0bM2NA6AeX6GXPOffXI= -is-valid-glob@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/is-valid-glob/-/is-valid-glob-1.0.0.tgz#29bf3eff701be2d4d315dbacc39bc39fe8f601aa" - integrity sha1-Kb8+/3Ab4tTTFdusw5vDn+j2Aao= - is-window@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-window/-/is-window-1.0.2.tgz#2c896ca53db97de45d3c33133a65d8c9f563480d" integrity sha1-LIlspT25feRdPDMTOmXYyfVjSA0= -is-windows@^1.0.1, is-windows@^1.0.2: +is-windows@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/is-windows/-/is-windows-1.0.2.tgz#d1850eb9791ecd18e6182ce12a30f396634bb19d" integrity sha512-eXK1UInq2bPmjyX6e3VHIzMLobc4J94i4AWn+Hpq3OU5KkrRC96OAcR3PRJ/pGu6m8TRnBHP9dkXQVsT/COVIA== @@ -12601,11 +12160,6 @@ just-curry-it@^3.1.0: resolved "https://registry.yarnpkg.com/just-curry-it/-/just-curry-it-3.1.0.tgz#ab59daed308a58b847ada166edd0a2d40766fbc5" integrity sha512-mjzgSOFzlrurlURaHVjnQodyPNvrHrf1TbQP2XU9NSqBtHQPuHZ+Eb6TAJP7ASeJN9h9K0KXoRTs8u6ouHBKvg== -just-debounce@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/just-debounce/-/just-debounce-1.0.0.tgz#87fccfaeffc0b68cd19d55f6722943f929ea35ea" - integrity sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= - keccak256@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/keccak256/-/keccak256-1.0.0.tgz#1ba55ce78ed3d63fb7091d045469007da984171d" @@ -12673,7 +12227,7 @@ kind-of@^4.0.0: dependencies: is-buffer "^1.1.5" -kind-of@^5.0.0, kind-of@^5.0.2: +kind-of@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/kind-of/-/kind-of-5.1.0.tgz#729c91e2d857b7a419a1f9aa65685c4c33f5845d" integrity sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw== @@ -12715,14 +12269,6 @@ last-call-webpack-plugin@^3.0.0: lodash "^4.17.5" webpack-sources "^1.1.0" -last-run@^1.1.0: - version "1.1.1" - resolved "https://registry.yarnpkg.com/last-run/-/last-run-1.1.1.tgz#45b96942c17b1c79c772198259ba943bebf8ca5b" - integrity sha1-RblpQsF7HHnHchmCWbqUO+v4yls= - dependencies: - default-resolution "^2.0.0" - es6-weak-map "^2.0.1" - latest-version@^5.0.0: version "5.1.0" resolved "https://registry.yarnpkg.com/latest-version/-/latest-version-5.1.0.tgz#119dfe908fe38d15dfa43ecd13fa12ec8832face" @@ -12756,32 +12302,11 @@ lazy-val@^1.0.4: resolved "https://registry.yarnpkg.com/lazy-val/-/lazy-val-1.0.4.tgz#882636a7245c2cfe6e0a4e3ba6c5d68a137e5c65" integrity sha512-u93kb2fPbIrfzBuLjZE+w+fJbUUMhNDXxNmMfaqNgpfQf1CO5ZSe2LfsnBqVAk7i/2NF48OSoRj+Xe2VT+lE8Q== -lazystream@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lazystream/-/lazystream-1.0.0.tgz#f6995fe0f820392f61396be89462407bb77168e4" - integrity sha1-9plf4PggOS9hOWvolGJAe7dxaOQ= - dependencies: - readable-stream "^2.0.5" - -lcid@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lcid/-/lcid-1.0.0.tgz#308accafa0bc483a3867b4b6f2b9506251d1b835" - integrity sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= - dependencies: - invert-kv "^1.0.0" - lcov-parse@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lcov-parse/-/lcov-parse-1.0.0.tgz#eb0d46b54111ebc561acb4c408ef9363bdc8f7e0" integrity sha1-6w1GtUER68VhrLTECO+TY73I9+A= -lead@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/lead/-/lead-1.0.0.tgz#6f14f99a37be3a9dd784f5495690e5903466ee42" - integrity sha1-bxT5mje+Op3XhPVJVpDlkDRm7kI= - dependencies: - flush-write-stream "^1.0.2" - left-pad@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/left-pad/-/left-pad-1.3.0.tgz#5b8a3a7765dfe001261dde915589e782f8c94d1e" @@ -12857,20 +12382,6 @@ levn@^0.3.0, levn@~0.3.0: prelude-ls "~1.1.2" type-check "~0.3.2" -liftoff@^3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/liftoff/-/liftoff-3.1.0.tgz#c9ba6081f908670607ee79062d700df062c52ed3" - integrity sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog== - dependencies: - extend "^3.0.0" - findup-sync "^3.0.0" - fined "^1.0.1" - flagged-respawn "^1.0.0" - is-plain-object "^2.0.4" - object.map "^1.0.0" - rechoir "^0.6.2" - resolve "^1.1.7" - lines-and-columns@^1.1.6: version "1.1.6" resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00" @@ -13233,13 +12744,6 @@ make-dir@^3.0.0, make-dir@^3.0.2: dependencies: semver "^6.0.0" -make-iterator@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/make-iterator/-/make-iterator-1.0.1.tgz#29b33f312aa8f547c4a5e490f56afcec99133ad6" - integrity sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw== - dependencies: - kind-of "^6.0.2" - makeerror@1.0.x: version "1.0.11" resolved "https://registry.yarnpkg.com/makeerror/-/makeerror-1.0.11.tgz#e01a5c9109f2af79660e4e8b9587790184f5a96c" @@ -13252,7 +12756,7 @@ mamacro@^0.0.3: resolved "https://registry.yarnpkg.com/mamacro/-/mamacro-0.0.3.tgz#ad2c9576197c9f1abf308d0787865bd975a3f3e4" integrity sha512-qMEwh+UujcQ+kbz3T6V+wAmO2U8veoq2w+3wY8MquqwVA3jChfwY+Tk52GZKDfACEPjuZ7r2oJLejwpt8jtwTA== -map-cache@^0.2.0, map-cache@^0.2.2: +map-cache@^0.2.2: version "0.2.2" resolved "https://registry.yarnpkg.com/map-cache/-/map-cache-0.2.2.tgz#c32abd0bd6525d9b051645bb4f26ac5dc98a0dbf" integrity sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= @@ -13287,16 +12791,6 @@ markdown-to-jsx@^6.11.4: prop-types "^15.6.2" unquote "^1.1.0" -matchdep@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/matchdep/-/matchdep-2.0.0.tgz#c6f34834a0d8dbc3b37c27ee8bbcb27c7775582e" - integrity sha1-xvNINKDY28OzfCfui7yyfHd1WC4= - dependencies: - findup-sync "^2.0.0" - micromatch "^3.0.4" - resolve "^1.4.0" - stack-trace "0.0.10" - matcher@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/matcher/-/matcher-3.0.0.tgz#bd9060f4c5b70aa8041ccc6f80368760994f30ca" @@ -13465,7 +12959,7 @@ microevent.ts@~0.1.1: resolved "https://registry.yarnpkg.com/microevent.ts/-/microevent.ts-0.1.1.tgz#70b09b83f43df5172d0205a63025bce0f7357fa0" integrity sha512-jo1OfR4TaEwd5HOrt5+tAZ9mqT4jmpNAusXtyfNzqVm9uiSYFZlKM1wYL4oU7azZW/PxQW53wM0S6OR1JHNa2g== -micromatch@^3.0.4, micromatch@^3.1.10, micromatch@^3.1.4: +micromatch@^3.1.10, micromatch@^3.1.4: version "3.1.10" resolved "https://registry.yarnpkg.com/micromatch/-/micromatch-3.1.10.tgz#70859bc95c9840952f359a068a3fc49f9ecfac23" integrity sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg== @@ -13832,11 +13326,6 @@ multihashes@^0.4.15, multihashes@~0.4.15: multibase "^0.7.0" varint "^5.0.0" -mute-stdout@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/mute-stdout/-/mute-stdout-1.0.1.tgz#acb0300eb4de23a7ddeec014e3e96044b3472331" - integrity sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg== - mute-stream@0.0.7: version "0.0.7" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" @@ -14150,13 +13639,6 @@ normalize-url@^4.1.0: prop-types "^15.7.2" react-is "^16.9.0" -now-and-later@^2.0.0: - version "2.0.1" - resolved "https://registry.yarnpkg.com/now-and-later/-/now-and-later-2.0.1.tgz#8e579c8685764a7cc02cb680380e94f43ccb1f7c" - integrity sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ== - dependencies: - once "^1.3.2" - npm-conf@^1.1.3: version "1.1.3" resolved "https://registry.yarnpkg.com/npm-conf/-/npm-conf-1.1.3.tgz#256cc47bd0e218c259c4e9550bf413bc2192aff9" @@ -14302,26 +13784,6 @@ object.assign@4.1.0, object.assign@^4.1.0: has-symbols "^1.0.0" object-keys "^1.0.11" -object.assign@^4.0.4: - version "4.1.1" - resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.1.tgz#303867a666cdd41936ecdedfb1f8f3e32a478cdd" - integrity sha512-VT/cxmx5yaoHSOTSyrCygIDFco+RsibY2NM0a4RdEeY/4KgqezwFtK1yr3U67xYhqJSlASm2pKhLVzPj2lr4bA== - dependencies: - define-properties "^1.1.3" - es-abstract "^1.18.0-next.0" - has-symbols "^1.0.1" - object-keys "^1.1.1" - -object.defaults@^1.0.0, object.defaults@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/object.defaults/-/object.defaults-1.1.0.tgz#3a7f868334b407dea06da16d88d5cd29e435fecf" - integrity sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= - dependencies: - array-each "^1.0.1" - array-slice "^1.0.0" - for-own "^1.0.0" - isobject "^3.0.0" - object.entries@^1.1.0, object.entries@^1.1.1, object.entries@^1.1.2: version "1.1.2" resolved "https://registry.yarnpkg.com/object.entries/-/object.entries-1.1.2.tgz#bc73f00acb6b6bb16c203434b10f9a7e797d3add" @@ -14349,29 +13811,13 @@ object.getownpropertydescriptors@^2.0.3, object.getownpropertydescriptors@^2.1.0 define-properties "^1.1.3" es-abstract "^1.17.0-next.1" -object.map@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.map/-/object.map-1.0.1.tgz#cf83e59dc8fcc0ad5f4250e1f78b3b81bd801d37" - integrity sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - -object.pick@^1.2.0, object.pick@^1.3.0: +object.pick@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/object.pick/-/object.pick-1.3.0.tgz#87a10ac4c1694bd2e1cbf53591a66141fb5dd747" integrity sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= dependencies: isobject "^3.0.1" -object.reduce@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/object.reduce/-/object.reduce-1.0.1.tgz#6fe348f2ac7fa0f95ca621226599096825bb03ad" - integrity sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= - dependencies: - for-own "^1.0.0" - make-iterator "^1.0.0" - object.values@^1.1.0, object.values@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object.values/-/object.values-1.1.1.tgz#68a99ecde356b7e9295a3c5e0ce31dc8c953de5e" @@ -14421,7 +13867,7 @@ on-headers@~1.0.2: resolved "https://registry.yarnpkg.com/on-headers/-/on-headers-1.0.2.tgz#772b0ae6aaa525c399e489adfad90c403eb3c28f" integrity sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA== -once@^1.3.0, once@^1.3.1, once@^1.3.2, once@^1.4.0: +once@^1.3.0, once@^1.3.1, once@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/once/-/once-1.4.0.tgz#583b1aa775961d4b113ac17d9c50baef9dd76bd1" integrity sha1-WDsap3WWHUsROsF9nFC6753Xa9E= @@ -14502,13 +13948,6 @@ optionator@^0.8.1, optionator@^0.8.3: type-check "~0.3.2" word-wrap "~1.2.3" -ordered-read-streams@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/ordered-read-streams/-/ordered-read-streams-1.0.1.tgz#77c0cb37c41525d64166d990ffad7ec6a0e1363e" - integrity sha1-d8DLN8QVJdZBZtmQ/61+xqDhNj4= - dependencies: - readable-stream "^2.0.1" - original-require@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/original-require/-/original-require-1.0.1.tgz#0f130471584cd33511c5ec38c8d59213f9ac5e20" @@ -14531,13 +13970,6 @@ os-homedir@^1.0.0: resolved "https://registry.yarnpkg.com/os-homedir/-/os-homedir-1.0.2.tgz#ffbc4988336e0e833de0c168c7ef152121aa7fb3" integrity sha1-/7xJiDNuDoM94MFox+8VISGqf7M= -os-locale@^1.4.0: - version "1.4.0" - resolved "https://registry.yarnpkg.com/os-locale/-/os-locale-1.4.0.tgz#20f9f17ae29ed345e8bde583b13d2009803c14d9" - integrity sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= - dependencies: - lcid "^1.0.0" - os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -14718,15 +14150,6 @@ parse-entities@^1.1.2: is-decimal "^1.0.0" is-hexadecimal "^1.0.0" -parse-filepath@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/parse-filepath/-/parse-filepath-1.0.2.tgz#a632127f53aaf3d15876f5872f3ffac763d6c891" - integrity sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= - dependencies: - is-absolute "^1.0.0" - map-cache "^0.2.0" - path-root "^0.1.1" - parse-headers@^2.0.0: version "2.0.3" resolved "https://registry.yarnpkg.com/parse-headers/-/parse-headers-2.0.3.tgz#5e8e7512383d140ba02f0c7aa9f49b4399c92515" @@ -14757,16 +14180,6 @@ parse-json@^5.0.0: json-parse-better-errors "^1.0.1" lines-and-columns "^1.1.6" -parse-node-version@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/parse-node-version/-/parse-node-version-1.0.1.tgz#e2b5dbede00e7fa9bc363607f53327e8b073189b" - integrity sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA== - -parse-passwd@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/parse-passwd/-/parse-passwd-1.0.0.tgz#6d5b934a456993b23d37f40a382d6f1666a8e5c6" - integrity sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= - parse5@4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/parse5/-/parse5-4.0.0.tgz#6d78656e3da8d78b4ec0b906f7c08ef1dfe3f608" @@ -14847,18 +14260,6 @@ path-parse@^1.0.6: resolved "https://registry.yarnpkg.com/path-parse/-/path-parse-1.0.6.tgz#d62dbb5679405d72c4737ec58600e9ddcf06d24c" integrity sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw== -path-root-regex@^0.1.0: - version "0.1.2" - resolved "https://registry.yarnpkg.com/path-root-regex/-/path-root-regex-0.1.2.tgz#bfccdc8df5b12dc52c8b43ec38d18d72c04ba96d" - integrity sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= - -path-root@^0.1.1: - version "0.1.1" - resolved "https://registry.yarnpkg.com/path-root/-/path-root-0.1.1.tgz#9a4a6814cac1c0cd73360a95f32083c8ea4745b7" - integrity sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= - dependencies: - path-root-regex "^0.1.0" - path-to-regexp@0.1.7: version "0.1.7" resolved "https://registry.yarnpkg.com/path-to-regexp/-/path-to-regexp-0.1.7.tgz#df604178005f522f15eb4490e7247a1bfaa67f8c" @@ -15878,7 +15279,7 @@ pretty-format@^26.4.2: ansi-styles "^4.0.0" react-is "^16.12.0" -pretty-hrtime@^1.0.0, pretty-hrtime@^1.0.3: +pretty-hrtime@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz#b7e3ea42435a4c9b2759d99e0f201eb195802ee1" integrity sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= @@ -15902,7 +15303,7 @@ private@^0.1.6, private@^0.1.8, private@~0.1.5: resolved "https://registry.yarnpkg.com/private/-/private-0.1.8.tgz#2381edb3689f7a53d653190060fcf822d2f368ff" integrity sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg== -process-nextick-args@^2.0.0, process-nextick-args@~2.0.0: +process-nextick-args@~2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz#7820d9b16120cc55ca9ae7792680ae7dba6d7fe2" integrity sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag== @@ -16049,7 +15450,7 @@ pump@^3.0.0: end-of-stream "^1.1.0" once "^1.3.1" -pumpify@^1.3.3, pumpify@^1.3.5: +pumpify@^1.3.3: version "1.5.1" resolved "https://registry.yarnpkg.com/pumpify/-/pumpify-1.5.1.tgz#36513be246ab27570b1a374a5ce278bfd74370ce" integrity sha512-oClZI37HvuUJJxSKKrC17bZ9Cu0ZYhEAGPsPUy9KlMUmv9dKX2o77RUmq7f3XjIxbwyGwYzbzQ1L2Ks8sIradQ== @@ -16765,7 +16166,7 @@ read-pkg@^4.0.1: parse-json "^4.0.0" pify "^3.0.0" -"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: +"readable-stream@1 || 2", readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.1.5, readable-stream@^2.2.2, readable-stream@^2.2.9, readable-stream@^2.3.0, readable-stream@^2.3.3, readable-stream@^2.3.5, readable-stream@^2.3.6, readable-stream@~2.3.6: version "2.3.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.7.tgz#1eca1cf711aef814c04f62252a36a62f6cb23b57" integrity sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw== @@ -17068,24 +16469,7 @@ relateurl@^0.2.7: resolved "https://registry.yarnpkg.com/relateurl/-/relateurl-0.2.7.tgz#54dbf377e51440aca90a4cd274600d3ff2d888a9" integrity sha1-VNvzd+UUQKypCkzSdGANP/LYiKk= -remove-bom-buffer@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/remove-bom-buffer/-/remove-bom-buffer-3.0.0.tgz#c2bf1e377520d324f623892e33c10cac2c252b53" - integrity sha512-8v2rWhaakv18qcvNeli2mZ/TMTL2nEyAKRvzo1WtnZBl15SHyEhrCu2/xKlJyUFKHiHgfXIyuY6g2dObJJycXQ== - dependencies: - is-buffer "^1.1.5" - is-utf8 "^0.2.1" - -remove-bom-stream@^1.2.0: - version "1.2.0" - resolved "https://registry.yarnpkg.com/remove-bom-stream/-/remove-bom-stream-1.2.0.tgz#05f1a593f16e42e1fb90ebf59de8e569525f9523" - integrity sha1-BfGlk/FuQuH7kOv1nejlaVJflSM= - dependencies: - remove-bom-buffer "^3.0.0" - safe-buffer "^5.1.0" - through2 "^2.0.3" - -remove-trailing-separator@^1.0.1, remove-trailing-separator@^1.1.0: +remove-trailing-separator@^1.0.1: version "1.1.0" resolved "https://registry.yarnpkg.com/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz#c24bce2a283adad5bc3f58e0d48249b92379d8ef" integrity sha1-wkvOKig62tW8P1jg1IJJuSN52O8= @@ -17118,20 +16502,6 @@ repeating@^2.0.0: dependencies: is-finite "^1.0.0" -replace-ext@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/replace-ext/-/replace-ext-1.0.1.tgz#2d6d996d04a15855d967443631dd5f77825b016a" - integrity sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw== - -replace-homedir@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/replace-homedir/-/replace-homedir-1.0.0.tgz#e87f6d513b928dde808260c12be7fec6ff6e798c" - integrity sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= - dependencies: - homedir-polyfill "^1.0.1" - is-absolute "^1.0.0" - remove-trailing-separator "^1.1.0" - request-promise-core@1.1.4: version "1.1.4" resolved "https://registry.yarnpkg.com/request-promise-core/-/request-promise-core-1.1.4.tgz#3eedd4223208d419867b78ce815167d10593a22f" @@ -17184,11 +16554,6 @@ require-from-string@^2.0.0: resolved "https://registry.yarnpkg.com/require-from-string/-/require-from-string-2.0.2.tgz#89a7fdd938261267318eafe14f9c32e598c36909" integrity sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw== -require-main-filename@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-1.0.1.tgz#97f717b69d48784f5f526a6c5aa8ffdda055a4d1" - integrity sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= - require-main-filename@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/require-main-filename/-/require-main-filename-2.0.0.tgz#d0b329ecc7cc0f61649f62215be69af54aa8989b" @@ -17216,14 +16581,6 @@ resolve-cwd@^2.0.0: dependencies: resolve-from "^3.0.0" -resolve-dir@^1.0.0, resolve-dir@^1.0.1: - version "1.0.1" - resolved "https://registry.yarnpkg.com/resolve-dir/-/resolve-dir-1.0.1.tgz#79a40644c362be82f26effe739c9bb5382046f43" - integrity sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= - dependencies: - expand-tilde "^2.0.0" - global-modules "^1.0.0" - resolve-from@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-3.0.0.tgz#b22c7af7d9d6881bc8b6e653335eebcb0a188748" @@ -17239,13 +16596,6 @@ resolve-from@^5.0.0: resolved "https://registry.yarnpkg.com/resolve-from/-/resolve-from-5.0.0.tgz#c35225843df8f776df21c57557bc087e9dfdfc69" integrity sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw== -resolve-options@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/resolve-options/-/resolve-options-1.1.0.tgz#32bb9e39c06d67338dc9378c0d6d6074566ad131" - integrity sha1-MrueOcBtZzONyTeMDW1gdFZq0TE= - dependencies: - value-or-function "^3.0.0" - resolve-pathname@^3.0.0: version "3.0.0" resolved "https://registry.yarnpkg.com/resolve-pathname/-/resolve-pathname-3.0.0.tgz#99d02224d3cf263689becbb393bc560313025dcd" @@ -17284,7 +16634,7 @@ resolve@1.15.0: dependencies: path-parse "^1.0.6" -resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.4.0, resolve@^1.8.1, resolve@~1.17.0: +resolve@^1.1.6, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.12.0, resolve@^1.13.1, resolve@^1.15.1, resolve@^1.17.0, resolve@^1.3.2, resolve@^1.8.1, resolve@~1.17.0: version "1.17.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444" integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w== @@ -17655,13 +17005,6 @@ semver-diff@^3.1.1: dependencies: semver "^6.3.0" -semver-greatest-satisfied-range@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz#13e8c2658ab9691cb0cd71093240280d36f77a5b" - integrity sha1-E+jCZYq5aRywzXEJMkAoDTb3els= - dependencies: - sver-compat "^1.5.0" - semver-regex@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/semver-regex/-/semver-regex-2.0.0.tgz#a93c2c5844539a770233379107b38c7b4ac9d338" @@ -18165,11 +17508,6 @@ space-separated-tokens@^1.0.0: resolved "https://registry.yarnpkg.com/space-separated-tokens/-/space-separated-tokens-1.1.5.tgz#85f32c3d10d9682007e917414ddc5c26d1aa6899" integrity sha512-q/JSVd1Lptzhf5bkYm4ob4iWPjx0KiRe3sRFBNrVqbJkFaBm5vbbowy1mymoPNLRa52+oadOhJ+K49wsSeSjTA== -sparkles@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/sparkles/-/sparkles-1.0.1.tgz#008db65edce6c50eec0c5e228e1945061dd0437c" - integrity sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw== - spawn-command@^0.0.2-1: version "0.0.2-1" resolved "https://registry.yarnpkg.com/spawn-command/-/spawn-command-0.0.2-1.tgz#62f5e9466981c1b796dc5929937e11c9c6921bd0" @@ -18317,11 +17655,6 @@ stable@^0.1.8: resolved "https://registry.yarnpkg.com/stable/-/stable-0.1.8.tgz#836eb3c8382fe2936feaf544631017ce7d47a3cf" integrity sha512-ji9qxRnOVfcuLDySj9qzhGSEFVobyt1kIOSkj1qZzYLzq7Tos/oUUWvotUPQLlrsidqsK6tBH89Bc9kL5zHA6w== -stack-trace@0.0.10: - version "0.0.10" - resolved "https://registry.yarnpkg.com/stack-trace/-/stack-trace-0.0.10.tgz#547c70b347e8d32b4e108ea1a2a159e5fdde19c0" - integrity sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= - stack-utils@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/stack-utils/-/stack-utils-1.0.2.tgz#33eba3897788558bebfc2db059dc158ec36cebb8" @@ -18383,11 +17716,6 @@ stream-each@^1.1.0: end-of-stream "^1.1.0" stream-shift "^1.0.0" -stream-exhaust@^1.0.1: - version "1.0.2" - resolved "https://registry.yarnpkg.com/stream-exhaust/-/stream-exhaust-1.0.2.tgz#acdac8da59ef2bc1e17a2c0ccf6c320d120e555d" - integrity sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw== - stream-http@^2.7.2: version "2.8.3" resolved "https://registry.yarnpkg.com/stream-http/-/stream-http-2.8.3.tgz#b2d242469288a5a27ec4fe8933acf623de6514fc" @@ -18440,7 +17768,7 @@ string-length@^3.1.0: astral-regex "^1.0.0" strip-ansi "^5.2.0" -string-width@^1.0.1, string-width@^1.0.2: +string-width@^1.0.1: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= @@ -18743,14 +18071,6 @@ supports-color@^6.1.0: dependencies: has-flag "^3.0.0" -sver-compat@^1.5.0: - version "1.5.0" - resolved "https://registry.yarnpkg.com/sver-compat/-/sver-compat-1.5.0.tgz#3cf87dfeb4d07b4a3f14827bc186b3fd0c645cd8" - integrity sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= - dependencies: - es6-iterator "^2.0.1" - es6-symbol "^3.1.1" - svg-parser@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/svg-parser/-/svg-parser-2.0.4.tgz#fdc2e29e13951736140b76cb122c8ee6630eb6b5" @@ -19025,15 +18345,7 @@ throttle-debounce@^2.1.0: resolved "https://registry.yarnpkg.com/throttle-debounce/-/throttle-debounce-2.2.1.tgz#fbd933ae6793448816f7d5b3cae259d464c98137" integrity sha512-i9hAVld1f+woAiyNGqWelpDD5W1tpMroL3NofTz9xzwq6acWBlO2dC8k5EFSZepU6oOINtV5Q3aSPoRg7o4+fA== -through2-filter@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/through2-filter/-/through2-filter-3.0.0.tgz#700e786df2367c2c88cd8aa5be4cf9c1e7831254" - integrity sha512-jaRjI2WxN3W1V8/FMZ9HKIBXixtiqs3SQSX4/YGIiP3gL6djW48VoZq9tDqeCWs3MT8YY5wb/zli8VW8snY1CA== - dependencies: - through2 "~2.0.0" - xtend "~4.0.0" - -through2@^2.0.0, through2@^2.0.3, through2@~2.0.0: +through2@^2.0.0, through2@^2.0.3: version "2.0.5" resolved "https://registry.yarnpkg.com/through2/-/through2-2.0.5.tgz#01c1e39eb31d07cb7d03a96a70823260b23132cd" integrity sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ== @@ -19051,11 +18363,6 @@ thunky@^1.0.2: resolved "https://registry.yarnpkg.com/thunky/-/thunky-1.1.0.tgz#5abaf714a9405db0504732bbccd2cedd9ef9537d" integrity sha512-eHY7nBftgThBqOyHGVN+l8gF0BucP09fMo0oO/Lb0w1OF80dJv+lDVpXG60WMQvkcxAkNybKsrEIE3ZtKGmPrA== -time-stamp@^1.0.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/time-stamp/-/time-stamp-1.1.0.tgz#764a5a11af50561921b133f3b44e618687e0f5c3" - integrity sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= - timed-out@^4.0.0, timed-out@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/timed-out/-/timed-out-4.0.1.tgz#f32eacac5a175bea25d7fab565ab3ed8741ef56f" @@ -19100,14 +18407,6 @@ tmpl@1.0.x: resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" integrity sha1-I2QN17QtAEM5ERQIIOXPRA5SHdE= -to-absolute-glob@^2.0.0: - version "2.0.2" - resolved "https://registry.yarnpkg.com/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz#1865f43d9e74b0822db9f145b78cff7d0f7c849b" - integrity sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= - dependencies: - is-absolute "^1.0.0" - is-negated-glob "^1.0.0" - to-arraybuffer@^1.0.0: version "1.0.1" resolved "https://registry.yarnpkg.com/to-arraybuffer/-/to-arraybuffer-1.0.1.tgz#7d229b1fcc637e466ca081180836a7aabff83f43" @@ -19191,13 +18490,6 @@ to-space-case@^1.0.0: dependencies: to-no-case "^1.0.0" -to-through@^2.0.0: - version "2.0.0" - resolved "https://registry.yarnpkg.com/to-through/-/to-through-2.0.0.tgz#fc92adaba072647bc0b67d6b03664aa195093af6" - integrity sha1-/JKtq6ByZHvAtn1rA2ZKoZUJOvY= - dependencies: - through2 "^2.0.3" - toggle-selection@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/toggle-selection/-/toggle-selection-1.0.6.tgz#6e45b1263f2017fa0acc7d89d78b15b8bf77da32" @@ -19512,37 +18804,11 @@ unbzip2-stream@^1.0.9: buffer "^5.2.1" through "^2.3.8" -unc-path-regex@^0.1.2: - version "0.1.2" - resolved "https://registry.yarnpkg.com/unc-path-regex/-/unc-path-regex-0.1.2.tgz#e73dd3d7b0d7c5ed86fbac6b0ae7d8c6a69d50fa" - integrity sha1-5z3T17DXxe2G+6xrCufYxqadUPo= - underscore@1.9.1: version "1.9.1" resolved "https://registry.yarnpkg.com/underscore/-/underscore-1.9.1.tgz#06dce34a0e68a7babc29b365b8e74b8925203961" integrity sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg== -undertaker-registry@^1.0.0: - version "1.0.1" - resolved "https://registry.yarnpkg.com/undertaker-registry/-/undertaker-registry-1.0.1.tgz#5e4bda308e4a8a2ae584f9b9a4359a499825cc50" - integrity sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= - -undertaker@^1.2.1: - version "1.3.0" - resolved "https://registry.yarnpkg.com/undertaker/-/undertaker-1.3.0.tgz#363a6e541f27954d5791d6fa3c1d321666f86d18" - integrity sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg== - dependencies: - arr-flatten "^1.0.1" - arr-map "^2.0.0" - bach "^1.0.0" - collection-map "^1.0.0" - es6-weak-map "^2.0.1" - fast-levenshtein "^1.0.0" - last-run "^1.1.0" - object.defaults "^1.0.0" - object.reduce "^1.0.0" - undertaker-registry "^1.0.0" - unfetch@^4.1.0: version "4.1.0" resolved "https://registry.yarnpkg.com/unfetch/-/unfetch-4.1.0.tgz#6ec2dd0de887e58a4dee83a050ded80ffc4137db" @@ -19605,14 +18871,6 @@ unique-slug@^2.0.0: dependencies: imurmurhash "^0.1.4" -unique-stream@^2.0.2: - version "2.3.1" - resolved "https://registry.yarnpkg.com/unique-stream/-/unique-stream-2.3.1.tgz#c65d110e9a4adf9a6c5948b28053d9a8d04cbeac" - integrity sha512-2nY4TnBE70yoxHkDli7DMazpWiP7xMdCYqU2nBRO0UB+ZpEkGsSija7MvmvnZFUeC+mrgiUfcHSr3LmRFIg4+A== - dependencies: - json-stable-stringify-without-jsonify "^1.0.1" - through2-filter "^3.0.0" - unique-string@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/unique-string/-/unique-string-2.0.0.tgz#39c6451f81afb2749de2b233e3f7c5e8843bd89d" @@ -19863,13 +19121,6 @@ v8-compile-cache@^2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.1.tgz#54bc3cdd43317bca91e35dcaf305b1a7237de745" integrity sha512-8OQ9CL+VWyt3JStj7HX7/ciTL2V3Rl1Wf5OL+SNTm0yK1KvtReVulksyeRnCANHHuUxHlQig+JJDlUhBt1NQDQ== -v8flags@^3.2.0: - version "3.2.0" - resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-3.2.0.tgz#b243e3b4dfd731fa774e7492128109a0fe66d656" - integrity sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg== - dependencies: - homedir-polyfill "^1.0.1" - validate-npm-package-license@^3.0.1: version "3.0.4" resolved "https://registry.yarnpkg.com/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz#fc91f6b9c7ba15c857f4cb2c5defeec39d4f410a" @@ -19883,11 +19134,6 @@ value-equal@^1.0.1: resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" integrity sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw== -value-or-function@^3.0.0: - version "3.0.0" - resolved "https://registry.yarnpkg.com/value-or-function/-/value-or-function-3.0.0.tgz#1c243a50b595c1be54a754bfece8563b9ff8d813" - integrity sha1-HCQ6ULWVwb5Up1S/7OhWO5/42BM= - varint@^5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/varint/-/varint-5.0.0.tgz#d826b89f7490732fabc0c0ed693ed475dcb29ebf" @@ -19912,54 +19158,6 @@ verror@1.10.0: core-util-is "1.0.2" extsprintf "^1.2.0" -vinyl-fs@^3.0.0: - version "3.0.3" - resolved "https://registry.yarnpkg.com/vinyl-fs/-/vinyl-fs-3.0.3.tgz#c85849405f67428feabbbd5c5dbdd64f47d31bc7" - integrity sha512-vIu34EkyNyJxmP0jscNzWBSygh7VWhqun6RmqVfXePrOwi9lhvRs//dOaGOTRUQr4tx7/zd26Tk5WeSVZitgng== - dependencies: - fs-mkdirp-stream "^1.0.0" - glob-stream "^6.1.0" - graceful-fs "^4.0.0" - is-valid-glob "^1.0.0" - lazystream "^1.0.0" - lead "^1.0.0" - object.assign "^4.0.4" - pumpify "^1.3.5" - readable-stream "^2.3.3" - remove-bom-buffer "^3.0.0" - remove-bom-stream "^1.2.0" - resolve-options "^1.1.0" - through2 "^2.0.0" - to-through "^2.0.0" - value-or-function "^3.0.0" - vinyl "^2.0.0" - vinyl-sourcemap "^1.1.0" - -vinyl-sourcemap@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/vinyl-sourcemap/-/vinyl-sourcemap-1.1.0.tgz#92a800593a38703a8cdb11d8b300ad4be63b3e16" - integrity sha1-kqgAWTo4cDqM2xHYswCtS+Y7PhY= - dependencies: - append-buffer "^1.0.2" - convert-source-map "^1.5.0" - graceful-fs "^4.1.6" - normalize-path "^2.1.1" - now-and-later "^2.0.0" - remove-bom-buffer "^3.0.0" - vinyl "^2.0.0" - -vinyl@^2.0.0: - version "2.2.0" - resolved "https://registry.yarnpkg.com/vinyl/-/vinyl-2.2.0.tgz#d85b07da96e458d25b2ffe19fece9f2caa13ed86" - integrity sha512-MBH+yP0kC/GQ5GwBqrTPTzEfiiLjta7hTtvQtbxBgTeSXsmKQRQecjibMbxIXzVT3Y9KJK+drOz1/k+vsu8Nkg== - dependencies: - clone "^2.1.1" - clone-buffer "^1.0.0" - clone-stats "^1.0.0" - cloneable-readable "^1.0.0" - remove-trailing-separator "^1.0.1" - replace-ext "^1.0.0" - vm-browserify@^1.0.1: version "1.1.2" resolved "https://registry.yarnpkg.com/vm-browserify/-/vm-browserify-1.1.2.tgz#78641c488b8e6ca91a75f511e7a3b32a86e5dda0" @@ -21029,7 +20227,6 @@ websocket@^1.0.31: dependencies: debug "^2.2.0" es5-ext "^0.10.50" - gulp "^4.0.2" nan "^2.14.0" typedarray-to-buffer "^3.1.5" yaeti "^0.0.6" @@ -21074,11 +20271,6 @@ whatwg-url@^7.0.0: tr46 "^1.0.1" webidl-conversions "^4.0.2" -which-module@^1.0.0: - version "1.0.0" - resolved "https://registry.yarnpkg.com/which-module/-/which-module-1.0.0.tgz#bba63ca861948994ff307736089e3b96026c2a4f" - integrity sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= - which-module@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/which-module/-/which-module-2.0.0.tgz#d9ef07dce77b9902b8a3a8fa4b31c3e3f7e6e87a" @@ -21096,7 +20288,7 @@ which@2.0.2, which@^2.0.1: dependencies: isexe "^2.0.0" -which@^1.2.14, which@^1.2.9, which@^1.3.0, which@^1.3.1: +which@^1.2.9, which@^1.3.0, which@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/which/-/which-1.3.1.tgz#a45043d54f5805316da8d62f9f50918d3da70b0a" integrity sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ== @@ -21276,14 +20468,6 @@ workerpool@6.0.0: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.0.0.tgz#85aad67fa1a2c8ef9386a1b43539900f61d03d58" integrity sha512-fU2OcNA/GVAJLLyKUoHkAgIhKb0JoCpSjLC/G2vYKxUjVmQwGbRVeoPJ1a8U4pnVofz4AQV5Y/NEw8oKqxEBtA== -wrap-ansi@^2.0.0: - version "2.1.0" - resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-2.1.0.tgz#d8fc3d284dd05794fe84973caecdd1cf824fdd85" - integrity sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= - dependencies: - string-width "^1.0.1" - strip-ansi "^3.0.1" - wrap-ansi@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09" @@ -21437,11 +20621,6 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -y18n@^3.2.1: - version "3.2.1" - resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" - integrity sha1-bRX7qITAhnnA136I53WegR4H+kE= - y18n@^4.0.0: version "4.0.0" resolved "https://registry.yarnpkg.com/y18n/-/y18n-4.0.0.tgz#95ef94f85ecc81d007c264e190a120f0a3c8566b" @@ -21480,14 +20659,6 @@ yargs-parser@13.1.2, yargs-parser@^13.1.2: camelcase "^5.0.0" decamelize "^1.2.0" -yargs-parser@5.0.0-security.0: - version "5.0.0-security.0" - resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0-security.0.tgz#4ff7271d25f90ac15643b86076a2ab499ec9ee24" - integrity sha512-T69y4Ps64LNesYxeYGYPvfoMTt/7y1XtfpIslUeK4um+9Hu7hlGoRtaDLvdXb7+/tfq4opVa2HRY5xGip022rQ== - dependencies: - camelcase "^3.0.0" - object.assign "^4.1.0" - yargs-parser@^10.0.0: version "10.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-10.1.0.tgz#7202265b89f7e9e9f2e5765e0fe735a905edbaa8" @@ -21545,25 +20716,6 @@ yargs@^15.3.1: y18n "^4.0.0" yargs-parser "^18.1.2" -yargs@^7.1.0: - version "7.1.1" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.1.tgz#67f0ef52e228d4ee0d6311acede8850f53464df6" - integrity sha512-huO4Fr1f9PmiJJdll5kwoS2e4GqzGSsMT3PPMpOwoVkOK8ckqAewMTZyA6LXVQWflleb/Z8oPBEvNsMft0XE+g== - dependencies: - camelcase "^3.0.0" - cliui "^3.2.0" - decamelize "^1.1.1" - get-caller-file "^1.0.1" - os-locale "^1.4.0" - read-pkg-up "^1.0.1" - require-directory "^2.1.1" - require-main-filename "^1.0.1" - set-blocking "^2.0.0" - string-width "^1.0.2" - which-module "^1.0.0" - y18n "^3.2.1" - yargs-parser "5.0.0-security.0" - yauzl@^2.10.0, yauzl@^2.4.2: version "2.10.0" resolved "https://registry.yarnpkg.com/yauzl/-/yauzl-2.10.0.tgz#c7eb17c93e112cb1086fa6d8e51fb0667b79a5f9" From 46c59460cd237c18438dc8b18b93554ac87869d1 Mon Sep 17 00:00:00 2001 From: Mati Dastugue Date: Mon, 21 Sep 2020 12:26:15 -0300 Subject: [PATCH 02/11] ENS names not working properly (#1372) * Fix ENS names * Fix types * Add typo to AddressBookInput component * Remove ensToAddress field * Fix error message variable name Co-authored-by: Daniel Sanchez --- .../screens/AddressBookInput/index.tsx | 32 +++++++++++-------- 1 file changed, 18 insertions(+), 14 deletions(-) diff --git a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx index 74e41694..ddd0791d 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx @@ -1,5 +1,4 @@ import MuiTextField from '@material-ui/core/TextField' -import { withStyles } from '@material-ui/core/styles' import makeStyles from '@material-ui/core/styles/makeStyles' import Autocomplete from '@material-ui/lab/Autocomplete' import { List } from 'immutable' @@ -22,7 +21,7 @@ export interface AddressBookProps { pristine: boolean recipientAddress?: string setSelectedEntry: ( - entry: { address?: string; name?: string } | React.SetStateAction<{ address?: string; name?: string }> | null, + entry: { address?: string; name?: string } | React.SetStateAction<{ address?: string; name? }> | null, ) => void setIsValidAddress: (valid: boolean) => void } @@ -71,7 +70,7 @@ const AddressBookInput = ({ recipientAddress, setIsValidAddress, setSelectedEntry, -}: AddressBookProps) => { +}: AddressBookProps): React.ReactElement => { const classes = useStyles() const addressBook = useSelector(getAddressBook) const [isValidForm, setIsValidForm] = useState(true) @@ -84,9 +83,10 @@ const AddressBookInput = ({ const onAddressInputChanged = async (value: string): Promise => { const normalizedAddress = trimSpaces(value) + const isENSDomain = isValidEnsName(normalizedAddress) setInputAddValue(normalizedAddress) let resolvedAddress = normalizedAddress - let isValidText + let addressErrorMessage if (inputTouched && !normalizedAddress) { setIsValidForm(false) setValidationText('Required') @@ -94,13 +94,14 @@ const AddressBookInput = ({ return } if (normalizedAddress) { - if (isValidEnsName(normalizedAddress)) { + if (isENSDomain) { resolvedAddress = await getAddressFromENS(normalizedAddress) setInputAddValue(resolvedAddress) } - isValidText = mustBeEthereumAddress(resolvedAddress) - if (isCustomTx && isValidText === undefined) { - isValidText = await mustBeEthereumContractAddress(resolvedAddress) + + addressErrorMessage = mustBeEthereumAddress(resolvedAddress) + if (isCustomTx && addressErrorMessage === undefined) { + addressErrorMessage = await mustBeEthereumContractAddress(resolvedAddress) } // First removes the entries that are not contracts if the operation is custom tx @@ -114,14 +115,17 @@ const AddressBookInput = ({ ) }) setADBKList(filteredADBK) - if (!isValidText) { - setSelectedEntry({ address: normalizedAddress }) + if (!addressErrorMessage) { + setSelectedEntry({ + name: normalizedAddress, + address: resolvedAddress, + }) } } - setIsValidForm(isValidText === undefined) - setValidationText(isValidText) + setIsValidForm(addressErrorMessage === undefined) + setValidationText(addressErrorMessage) fieldMutator(resolvedAddress) - setIsValidAddress(isValidText === undefined) + setIsValidAddress(addressErrorMessage === undefined) } useEffect(() => { @@ -237,4 +241,4 @@ const AddressBookInput = ({ ) } -export default withStyles(styles as any)(AddressBookInput) +export default AddressBookInput From fffabf02cedad04645ca7c260a96f6b1895f9f03 Mon Sep 17 00:00:00 2001 From: nicolas Date: Mon, 21 Sep 2020 15:03:27 -0300 Subject: [PATCH 03/11] (Bugfix) - #1233 Fix method values length in TXs (#1314) * Fix method values length in TXs * arrays Improvment * Make use of EthHashInfo * review change --- .../TxDescription/CustomDescription.tsx | 43 +++++++---- .../ExpandedTx/TxDescription/Value.tsx | 73 +++++++------------ .../TxsTable/ExpandedTx/index.tsx | 2 +- .../Transactions/TxsTable/ExpandedTx/style.ts | 4 + 4 files changed, 59 insertions(+), 63 deletions(-) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx index a4a8199c..489451af 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx @@ -1,4 +1,4 @@ -import { IconText, Text } from '@gnosis.pm/safe-react-components' +import { IconText, Text, EthHashInfo } from '@gnosis.pm/safe-react-components' import { makeStyles } from '@material-ui/core/styles' import React from 'react' import styled from 'styled-components' @@ -12,8 +12,6 @@ import { MultiSendDetails, } from 'src/routes/safe/store/actions/transactions/utils/multiSendDecodedDetails' import Bold from 'src/components/layout/Bold' -import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' -import EtherscanLink from 'src/components/EtherscanLink' import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue' import Collapse from 'src/components/Collapse' import { useSelector } from 'react-redux' @@ -24,6 +22,8 @@ import { shortVersionOf } from 'src/logic/wallets/ethAddresses' import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { DataDecoded } from 'src/routes/safe/store/models/types/transactions.d' import DividerLine from 'src/components/DividerLine' +import { isArrayParameter } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils' +import { getNetwork } from 'src/config' export const TRANSACTIONS_DESC_CUSTOM_VALUE_TEST_ID = 'tx-description-custom-value' export const TRANSACTIONS_DESC_CUSTOM_DATA_TEST_ID = 'tx-description-custom-data' @@ -34,9 +34,14 @@ const useStyles = makeStyles(styles) const TxDetailsMethodName = styled(Text)` text-indent: 4px; ` -const TxDetailsMethodParam = styled.div` - text-indent: 8px; - display: flex; +const TxDetailsMethodParam = styled.div<{ isArrayParameter: boolean }>` + padding-left: 8px; + display: ${({ isArrayParameter }) => (isArrayParameter ? 'block' : 'flex')}; + align-items: center; + + p:first-of-type { + margin-right: ${({ isArrayParameter }) => (isArrayParameter ? '0' : '4px')}; + } ` const TxDetailsContent = styled.div` padding: 8px 8px 8px 16px; @@ -46,6 +51,10 @@ const TxInfo = styled.div` padding: 8px 8px 8px 16px; ` +const StyledMethodName = styled(Text)` + white-space: nowrap; +` + const TxInfoDetails = ({ data }: { data: DataDecoded }): React.ReactElement => ( @@ -53,10 +62,10 @@ const TxInfoDetails = ({ data }: { data: DataDecoded }): React.ReactElement => ( {data.parameters.map((param, index) => ( - - + + {param.name}({param.type}): - + ))} @@ -76,7 +85,7 @@ const MultiSendCustomDataAction = ({ tx, order }: { tx: MultiSendDetails; order: Send {humanReadableValue(tx.value)} ETH to: - + {!!tx.data && } @@ -173,11 +182,15 @@ const GenericCustomData = ({ amount = '0', data, recipient, storedTx }: GenericC Send {amount} to: - {recipientName ? ( - - ) : ( - - )} + + {!!storedTx?.dataDecoded && } diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx index 409440a5..7a0108e2 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/Value.tsx @@ -1,21 +1,15 @@ -import { Text } from '@gnosis.pm/safe-react-components' -import { makeStyles } from '@material-ui/core/styles' +import { Text, EthHashInfo } from '@gnosis.pm/safe-react-components' import React from 'react' import styled from 'styled-components' -import { styles } from './styles' - +import { getNetwork } from 'src/config' import { isAddress, isArrayParameter, } from 'src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/utils' -import { useWindowDimensions } from 'src/logic/hooks/useWindowDimensions' -import SafeEtherscanLink from 'src/components/EtherscanLink' - -const useStyles = makeStyles(styles) const NestedWrapper = styled.div` - text-indent: 24px; + padding-left: 4px; ` const StyledText = styled(Text)` @@ -28,53 +22,38 @@ interface RenderValueProps { value: string | string[] } -const EtherscanLink = ({ method, type, value }: RenderValueProps): React.ReactElement => { - const classes = useStyles() - const [cut, setCut] = React.useState(0) - const { width } = useWindowDimensions() - - React.useEffect(() => { - if (width <= 900) { - setCut(4) - } else if (width <= 1024) { - setCut(8) - } else { - setCut(12) - } - }, [width]) - - if (isArrayParameter(type)) { - return ( - - {(value as string[]).map((value, index) => ( - - ))} - - ) - } - - return -} - const GenericValue = ({ method, type, value }: RenderValueProps): React.ReactElement => { - if (isArrayParameter(type)) { - return ( + const getTextValue = (value: string) => {value} + + const getArrayValue = (parentId: string, value: string[] | string) => ( +
+ [ - {(value as string[]).map((value, index) => ( - - {value} - - ))} + {(value as string[]).map((currentValue, index) => { + const key = `${parentId}-value-${index}` + return ( +
+ {Array.isArray(currentValue) ? getArrayValue(key, currentValue) : getTextValue(currentValue)} +
+ ) + })}
- ) + ] +
+ ) + + if (isArrayParameter(type) || Array.isArray(value)) { + return getArrayValue(method, value) } - return {value as string} + return getTextValue(value as string) } const Value = ({ type, ...props }: RenderValueProps): React.ReactElement => { if (isAddress(type)) { - return + return ( + + ) } return diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx index 7bd08ad8..2bc2c2bb 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx @@ -63,7 +63,7 @@ const ExpandedTx = ({ cancelTx, tx }: ExpandedTxProps): React.ReactElement => { <> - +
Hash: diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts index f149263b..d76074be 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/style.ts @@ -1,6 +1,10 @@ import { border, lg, md } from 'src/theme/variables' const cssStyles = { + col: { + wordBreak: 'break-word', + whiteSpace: 'normal', + }, expandedTxBlock: { borderBottom: `2px solid ${border}`, }, From 59dc1f711c246408e26ca8cde160a017b5c04746 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 22 Sep 2020 09:31:07 -0300 Subject: [PATCH 04/11] (Bugfix) - #1246 Addressbook entries removed when reloading page (#1300) * Fix addressbook types Restructure addressbook store type * Add more safe types * Fix imports * Removes .toJS() usage * Fix condition for saving addressBook * Types & remove send button from addressbook if user not an owner * Add types for addressBook actions Remove unused saveAndUpdateAddressBook action * Refactor addressBook: make it global and removes immutableJS Add types Removes unused addAddressBook action * Remove todo * Fix edit and remove entries style when user is not owner * Adds and updates safe name in addressBook * Adds checkIfOwnerWasDeletedFromAddressBook Let the user remove owners users without adding them again each time the safe loads * Simplify loadAddressBookFromStorage * Fix compilation errors included in pr #1301 * Uses sameAddress function * Add migration function for old stored address books * Update tests * Replaces shouldAvoidUpdatesNotifications with addAddressBookEntryOptions on addAddressBookEntry * Update tests * Unify return on getOwnersWithNameFromAddressBook * Reword shouldAvoidUpdatesNotifications * Replaces adbk with addressBook * Fix condition * Fix typos * Fix typo Co-authored-by: Daniel Sanchez --- src/logic/addressBook/model/addressBook.ts | 20 +-- .../store/actions/addAddressBook.ts | 8 - .../store/actions/addAddressBookEntry.ts | 21 ++- .../actions/addOrUpdateAddressBookEntry.ts | 4 +- .../store/actions/loadAddressBook.ts | 3 +- .../actions/loadAddressBookFromStorage.ts | 20 +-- .../store/actions/removeAddressBookEntry.ts | 2 +- .../store/actions/saveAndUpdateAddressBook.ts | 15 -- .../store/actions/updateAddressBookEntry.ts | 3 +- .../store/middleware/addressBookMiddleware.ts | 10 +- .../addressBook/store/reducer/addressBook.ts | 133 +++++------------ .../store/reducer/types/addressBook.d.ts | 24 --- .../addressBook/store/selectors/index.ts | 24 +-- .../utils/__tests__/addressBookUtils.test.ts | 141 +++++++++++++++--- src/logic/addressBook/utils/index.ts | 69 +++++++-- src/logic/safe/store/actions/addSafe.ts | 5 +- .../store/actions/loadSafesFromStorage.ts | 2 +- .../safe/store/middleware/safeStorage.ts | 67 ++++++++- .../safe/store/reducer/allTransactions.ts | 7 +- .../CreateEditEntryModal/index.tsx | 8 +- .../safe/components/AddressBook/columns.ts | 13 +- .../safe/components/AddressBook/index.tsx | 108 +++++++------- .../safe/components/AddressBook/style.ts | 11 +- .../screens/AddressBookInput/index.tsx | 24 +-- .../screens/SendCollectible/index.tsx | 24 +-- .../screens/SendCollectible/style.ts | 3 +- .../SendModal/screens/SendFunds/index.tsx | 12 +- .../ManageOwners/EditOwnerModal/index.tsx | 4 +- .../OwnerAddressTableCell/index.tsx | 3 +- .../Settings/ManageOwners/index.tsx | 6 +- src/routes/safe/components/Settings/index.tsx | 4 +- src/store/index.ts | 12 +- 32 files changed, 460 insertions(+), 350 deletions(-) delete mode 100644 src/logic/addressBook/store/actions/addAddressBook.ts delete mode 100644 src/logic/addressBook/store/actions/saveAndUpdateAddressBook.ts delete mode 100644 src/logic/addressBook/store/reducer/types/addressBook.d.ts diff --git a/src/logic/addressBook/model/addressBook.ts b/src/logic/addressBook/model/addressBook.ts index 34554330..8575448f 100644 --- a/src/logic/addressBook/model/addressBook.ts +++ b/src/logic/addressBook/model/addressBook.ts @@ -1,15 +1,17 @@ -import { Record, RecordOf } from 'immutable' - -export interface AddressBookEntryProps { +export type AddressBookEntry = { address: string name: string - isOwner: boolean } -export const makeAddressBookEntry = Record({ - address: '', - name: '', - isOwner: false, +export const makeAddressBookEntry = ({ + address = '', + name = '', +}: { + address: string + name?: string +}): AddressBookEntry => ({ + address, + name, }) -export type AddressBookEntryRecord = RecordOf +export type AddressBookState = AddressBookEntry[] diff --git a/src/logic/addressBook/store/actions/addAddressBook.ts b/src/logic/addressBook/store/actions/addAddressBook.ts deleted file mode 100644 index 7f524bbd..00000000 --- a/src/logic/addressBook/store/actions/addAddressBook.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { createAction } from 'redux-actions' - -export const ADD_ADDRESS_BOOK = 'ADD_ADDRESS_BOOK' - -export const addAddressBook = createAction(ADD_ADDRESS_BOOK, (addressBook, safeAddress) => ({ - addressBook, - safeAddress, -})) diff --git a/src/logic/addressBook/store/actions/addAddressBookEntry.ts b/src/logic/addressBook/store/actions/addAddressBookEntry.ts index 76041d67..b3d80b50 100644 --- a/src/logic/addressBook/store/actions/addAddressBookEntry.ts +++ b/src/logic/addressBook/store/actions/addAddressBookEntry.ts @@ -1,7 +1,22 @@ import { createAction } from 'redux-actions' +import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' export const ADD_ENTRY = 'ADD_ENTRY' -export const addAddressBookEntry = createAction(ADD_ENTRY, (entry) => ({ - entry, -})) +type addAddressBookEntryOptions = { + notifyEntryUpdate: boolean +} + +export const addAddressBookEntry = createAction( + ADD_ENTRY, + (entry: AddressBookEntry, options: addAddressBookEntryOptions) => { + let notifyEntryUpdate = true + if (options) { + notifyEntryUpdate = options.notifyEntryUpdate + } + return { + entry, + shouldAvoidUpdatesNotifications: !notifyEntryUpdate, + } + }, +) diff --git a/src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry.ts b/src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry.ts index f88f9b50..f19a0078 100644 --- a/src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry.ts +++ b/src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry.ts @@ -1,8 +1,8 @@ import { createAction } from 'redux-actions' +import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' export const ADD_OR_UPDATE_ENTRY = 'ADD_OR_UPDATE_ENTRY' -export const addOrUpdateAddressBookEntry = createAction(ADD_OR_UPDATE_ENTRY, (entryAddress, entry) => ({ - entryAddress, +export const addOrUpdateAddressBookEntry = createAction(ADD_OR_UPDATE_ENTRY, (entry: AddressBookEntry) => ({ entry, })) diff --git a/src/logic/addressBook/store/actions/loadAddressBook.ts b/src/logic/addressBook/store/actions/loadAddressBook.ts index 6486d1cb..d887d198 100644 --- a/src/logic/addressBook/store/actions/loadAddressBook.ts +++ b/src/logic/addressBook/store/actions/loadAddressBook.ts @@ -1,7 +1,8 @@ import { createAction } from 'redux-actions' +import { AddressBookState } from 'src/logic/addressBook/model/addressBook' export const LOAD_ADDRESS_BOOK = 'LOAD_ADDRESS_BOOK' -export const loadAddressBook = createAction(LOAD_ADDRESS_BOOK, (addressBook) => ({ +export const loadAddressBook = createAction(LOAD_ADDRESS_BOOK, (addressBook: AddressBookState) => ({ addressBook, })) diff --git a/src/logic/addressBook/store/actions/loadAddressBookFromStorage.ts b/src/logic/addressBook/store/actions/loadAddressBookFromStorage.ts index 0e40bae6..c4315e3c 100644 --- a/src/logic/addressBook/store/actions/loadAddressBookFromStorage.ts +++ b/src/logic/addressBook/store/actions/loadAddressBookFromStorage.ts @@ -1,29 +1,17 @@ -import { List } from 'immutable' - import { loadAddressBook } from 'src/logic/addressBook/store/actions/loadAddressBook' import { buildAddressBook } from 'src/logic/addressBook/store/reducer/addressBook' import { getAddressBookFromStorage } from 'src/logic/addressBook/utils' -import { safesListSelector } from 'src/logic/safe/store/selectors' +import { Dispatch } from 'redux' -const loadAddressBookFromStorage = () => async (dispatch, getState) => { +const loadAddressBookFromStorage = () => async (dispatch: Dispatch): Promise => { try { - const state = getState() let storedAdBk = await getAddressBookFromStorage() if (!storedAdBk) { storedAdBk = [] } - let addressBook = buildAddressBook(storedAdBk) - // Fetch all the current safes, in case that we don't have a safe on the adbk, we add it - const safes = safesListSelector(state) - const adbkEntries = addressBook.keySeq().toArray() - safes.forEach((safe) => { - const { address } = safe - const found = adbkEntries.includes(address) - if (!found) { - addressBook = addressBook.set(address, List([])) - } - }) + const addressBook = buildAddressBook(storedAdBk) + dispatch(loadAddressBook(addressBook)) } catch (err) { // eslint-disable-next-line diff --git a/src/logic/addressBook/store/actions/removeAddressBookEntry.ts b/src/logic/addressBook/store/actions/removeAddressBookEntry.ts index a241e1c1..16571f47 100644 --- a/src/logic/addressBook/store/actions/removeAddressBookEntry.ts +++ b/src/logic/addressBook/store/actions/removeAddressBookEntry.ts @@ -2,6 +2,6 @@ import { createAction } from 'redux-actions' export const REMOVE_ENTRY = 'REMOVE_ENTRY' -export const removeAddressBookEntry = createAction(REMOVE_ENTRY, (entryAddress) => ({ +export const removeAddressBookEntry = createAction(REMOVE_ENTRY, (entryAddress: string) => ({ entryAddress, })) diff --git a/src/logic/addressBook/store/actions/saveAndUpdateAddressBook.ts b/src/logic/addressBook/store/actions/saveAndUpdateAddressBook.ts deleted file mode 100644 index 960ee92b..00000000 --- a/src/logic/addressBook/store/actions/saveAndUpdateAddressBook.ts +++ /dev/null @@ -1,15 +0,0 @@ -import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' -import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' -import { saveAddressBook } from 'src/logic/addressBook/utils' - -const saveAndUpdateAddressBook = (addressBook) => async (dispatch) => { - try { - dispatch(updateAddressBookEntry(makeAddressBookEntry(addressBook))) - await saveAddressBook(addressBook) - } catch (err) { - // eslint-disable-next-line - console.error('Error while loading active tokens from storage:', err) - } -} - -export default saveAndUpdateAddressBook diff --git a/src/logic/addressBook/store/actions/updateAddressBookEntry.ts b/src/logic/addressBook/store/actions/updateAddressBookEntry.ts index 38f8263a..a426f812 100644 --- a/src/logic/addressBook/store/actions/updateAddressBookEntry.ts +++ b/src/logic/addressBook/store/actions/updateAddressBookEntry.ts @@ -1,7 +1,8 @@ import { createAction } from 'redux-actions' +import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' export const UPDATE_ENTRY = 'UPDATE_ENTRY' -export const updateAddressBookEntry = createAction(UPDATE_ENTRY, (entry) => ({ +export const updateAddressBookEntry = createAction(UPDATE_ENTRY, (entry: AddressBookEntry) => ({ entry, })) diff --git a/src/logic/addressBook/store/middleware/addressBookMiddleware.ts b/src/logic/addressBook/store/middleware/addressBookMiddleware.ts index 9765be48..fb7075dd 100644 --- a/src/logic/addressBook/store/middleware/addressBookMiddleware.ts +++ b/src/logic/addressBook/store/middleware/addressBookMiddleware.ts @@ -2,7 +2,7 @@ import { ADD_ENTRY } from 'src/logic/addressBook/store/actions/addAddressBookEnt import { ADD_OR_UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' import { REMOVE_ENTRY } from 'src/logic/addressBook/store/actions/removeAddressBookEntry' import { UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' -import { addressBookMapSelector } from 'src/logic/addressBook/store/selectors' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { saveAddressBook } from 'src/logic/addressBook/utils' import { enhanceSnackbarForAction, getNotificationsFromTxType } from 'src/logic/notifications' import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar' @@ -16,15 +16,15 @@ const addressBookMiddleware = (store) => (next) => async (action) => { if (watchedActions.includes(action.type)) { const state = store.getState() const { dispatch } = store - const addressBook = addressBookMapSelector(state) - if (addressBook) { + const addressBook = addressBookSelector(state) + if (addressBook.length) { await saveAddressBook(addressBook) } switch (action.type) { case ADD_ENTRY: { - const { isOwner } = action.payload.entry - if (!isOwner) { + const { shouldAvoidUpdatesNotifications } = action.payload + if (!shouldAvoidUpdatesNotifications) { const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.ADDRESSBOOK_NEW_ENTRY) dispatch(enqueueSnackbar(enhanceSnackbarForAction(notification.afterExecution.noMoreConfirmationsNeeded))) } diff --git a/src/logic/addressBook/store/reducer/addressBook.ts b/src/logic/addressBook/store/reducer/addressBook.ts index e1ce17d6..55f3a493 100644 --- a/src/logic/addressBook/store/reducer/addressBook.ts +++ b/src/logic/addressBook/store/reducer/addressBook.ts @@ -1,134 +1,71 @@ -import { List, Map } from 'immutable' import { handleActions } from 'redux-actions' -import { - AddressBookEntryRecord, - AddressBookEntryProps, - makeAddressBookEntry, -} from 'src/logic/addressBook/model/addressBook' -import { ADD_ADDRESS_BOOK } from 'src/logic/addressBook/store/actions/addAddressBook' +import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { ADD_ENTRY } from 'src/logic/addressBook/store/actions/addAddressBookEntry' import { ADD_OR_UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' import { LOAD_ADDRESS_BOOK } from 'src/logic/addressBook/store/actions/loadAddressBook' import { REMOVE_ENTRY } from 'src/logic/addressBook/store/actions/removeAddressBookEntry' import { UPDATE_ENTRY } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' -import { getAddressesListFromSafeAddressBook } from 'src/logic/addressBook/utils' -import { sameAddress } from 'src/logic/wallets/ethAddresses' import { checksumAddress } from 'src/utils/checksumAddress' +import { getValidAddressBookName } from 'src/logic/addressBook/utils' export const ADDRESS_BOOK_REDUCER_ID = 'addressBook' -export type AddressBookCollection = List -export type AddressBookState = Map> - -export const buildAddressBook = (storedAdbk: AddressBookEntryProps[]): Map => { - let addressBookBuilt: Map = Map([]) - Object.entries(storedAdbk).forEach((adbkProps: any) => { - const safeAddress = checksumAddress(adbkProps[0]) - const adbkRecords: AddressBookEntryRecord[] = adbkProps[1].map(makeAddressBookEntry) - const adbkSafeEntries = List(adbkRecords) - addressBookBuilt = addressBookBuilt.set(safeAddress, adbkSafeEntries) +export const buildAddressBook = (storedAdbk: AddressBookState): AddressBookState => { + return storedAdbk.map((addressBookEntry) => { + const { address, name } = addressBookEntry + return makeAddressBookEntry({ address: checksumAddress(address), name }) }) - return addressBookBuilt } export default handleActions( { [LOAD_ADDRESS_BOOK]: (state, action) => { const { addressBook } = action.payload - return state.set('addressBook', addressBook) - }, - [ADD_ADDRESS_BOOK]: (state, action) => { - const { addressBook, safeAddress } = action.payload - // Adds the address book if it does not exists - const found = state.getIn(['addressBook', safeAddress]) - if (!found) { - return state.setIn(['addressBook', safeAddress], addressBook) - } - return state + return addressBook }, [ADD_ENTRY]: (state, action) => { const { entry } = action.payload - // Adds the entry to all the safes (if it does not already exists) - const newState = state.withMutations((map) => { - const adbkMap = map.get('addressBook') + const entryFound = state.find((oldEntry) => oldEntry.address === entry.address) - if (adbkMap) { - adbkMap.keySeq().forEach((safeAddress) => { - const safeAddressBook = state.getIn(['addressBook', safeAddress]) - - if (safeAddressBook) { - const adbkAddressList = getAddressesListFromSafeAddressBook(safeAddressBook) - const found = adbkAddressList.includes(entry.address) - if (!found) { - const updatedSafeAdbkList = safeAddressBook.push(entry) - map.setIn(['addressBook', safeAddress], updatedSafeAdbkList) - } - } - }) - } - }) - return newState + // Only adds entries with valid names + const validName = getValidAddressBookName(entry.name) + if (!entryFound && validName) { + state.push(entry) + } + return state }, [UPDATE_ENTRY]: (state, action) => { const { entry } = action.payload - - // Updates the entry from all the safes - const newState = state.withMutations((map) => { - map - .get('addressBook') - .keySeq() - .forEach((safeAddress) => { - const entriesList = state.getIn(['addressBook', safeAddress]) - const entryIndex = entriesList.findIndex((entryItem) => sameAddress(entryItem.address, entry.address)) - const updatedEntriesList = entriesList.set(entryIndex, entry) - map.setIn(['addressBook', safeAddress], updatedEntriesList) - }) - }) - - return newState + const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entry.address) + if (entryIndex >= 0) { + state[entryIndex] = entry + } + return state }, [REMOVE_ENTRY]: (state, action) => { const { entryAddress } = action.payload - // Removes the entry from all the safes - const newState = state.withMutations((map) => { - map - .get('addressBook') - .keySeq() - .forEach((safeAddress) => { - const entriesList = state.getIn(['addressBook', safeAddress]) - const entryIndex = entriesList.findIndex((entry) => sameAddress(entry.address, entryAddress)) - const updatedEntriesList = entriesList.remove(entryIndex) - map.setIn(['addressBook', safeAddress], updatedEntriesList) - }) - }) - return newState + const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress) + state.splice(entryIndex, 1) + return state }, [ADD_OR_UPDATE_ENTRY]: (state, action) => { const { entry, entryAddress } = action.payload - // Adds or Updates the entry to all the safes - return state.withMutations((map) => { - const addressBook = map.get('addressBook') - if (addressBook) { - addressBook.keySeq().forEach((safeAddress) => { - const safeAddressBook = state.getIn(['addressBook', safeAddress]) - const entryIndex = safeAddressBook.findIndex((entryItem) => sameAddress(entryItem.address, entryAddress)) - - if (entryIndex !== -1) { - const updatedEntriesList = safeAddressBook.update(entryIndex, (currentEntry) => currentEntry.merge(entry)) - map.setIn(['addressBook', safeAddress], updatedEntriesList) - } else { - const updatedSafeAdbkList = safeAddressBook.push(makeAddressBookEntry(entry)) - map.setIn(['addressBook', safeAddress], updatedSafeAdbkList) - } - }) - } - }) + // Only updates entries with valid names + const validName = getValidAddressBookName(entry.name) + if (!validName) { + return state + } + const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress) + if (entryIndex >= 0) { + state[entryIndex] = entry + } else { + state.push(entry) + } + return state }, }, - Map({ - addressBook: Map({}), - }), + [], ) diff --git a/src/logic/addressBook/store/reducer/types/addressBook.d.ts b/src/logic/addressBook/store/reducer/types/addressBook.d.ts deleted file mode 100644 index 6f2f4c7a..00000000 --- a/src/logic/addressBook/store/reducer/types/addressBook.d.ts +++ /dev/null @@ -1,24 +0,0 @@ -import { AddressBookEntryRecord, AddressBookEntryProps } from 'src/logic/addressBook/model/addressBook' -import { Map, List } from 'immutable' - -export interface AddressBookReducerState { - addressBook: AddressBookMap -} - -interface AddressBookMapSerialized { - [key: string]: AddressBookEntryProps -} - -interface AddressBookReducerStateSerialized extends AddressBookReducerState { - addressBook: Record -} - -export interface AddressBookMap extends Map { - toJS(): AddressBookMapSerialized - get(key: string, notSetValue: unknown): List -} - -export interface AddressBookReducerMap extends Map { - toJS(): AddressBookReducerStateSerialized - get(key: K): AddressBookReducerState[K] -} diff --git a/src/logic/addressBook/store/selectors/index.ts b/src/logic/addressBook/store/selectors/index.ts index b03fbdf1..ffa4fbbe 100644 --- a/src/logic/addressBook/store/selectors/index.ts +++ b/src/logic/addressBook/store/selectors/index.ts @@ -1,36 +1,22 @@ import { AppReduxState } from 'src/store' -import { List } from 'immutable' + import { createSelector } from 'reselect' import { ADDRESS_BOOK_REDUCER_ID } from 'src/logic/addressBook/store/reducer/addressBook' -import { AddressBookMap } from 'src/logic/addressBook/store/reducer/types/addressBook.d' -import { AddressBookEntryRecord } from 'src/logic/addressBook/model/addressBook' -import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' -export const addressBookMapSelector = (state: AppReduxState): AddressBookMap => - state[ADDRESS_BOOK_REDUCER_ID].get('addressBook') +import { AddressBookState } from 'src/logic/addressBook/model/addressBook' -export const getAddressBook = createSelector( - addressBookMapSelector, - safeParamAddressFromStateSelector, - (addressBook, safeAddress) => { - let result: List = List([]) - if (addressBook && safeAddress) { - result = addressBook.get(safeAddress, List()) - } - return result - }, -) +export const addressBookSelector = (state: AppReduxState): AddressBookState => state[ADDRESS_BOOK_REDUCER_ID] export const getNameFromAddressBook = createSelector( - getAddressBook, + addressBookSelector, (_, address) => address, (addressBook, address) => { const adbkEntry = addressBook.find((addressBookItem) => addressBookItem.address === address) + if (adbkEntry) { return adbkEntry.name } - return 'UNKNOWN' }, ) diff --git a/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts b/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts index f9574c29..63599d72 100644 --- a/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts +++ b/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts @@ -1,25 +1,31 @@ -import { Map, List } from 'immutable' +import { List } from 'immutable' import { getAddressBookFromStorage, - getAddressesListFromSafeAddressBook, - getNameFromSafeAddressBook, + getAddressesListFromAddressBook, + getNameFromAddressBook, getOwnersWithNameFromAddressBook, + migrateOldAddressBook, + OldAddressBookEntry, + OldAddressBookType, saveAddressBook, } from 'src/logic/addressBook/utils/index' import { buildAddressBook } from 'src/logic/addressBook/store/reducer/addressBook' -import { AddressBookEntryRecord, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' -const getMockAddressBookEntry = ( - address: string, - name: string = 'test', - isOwner: boolean = false, -): AddressBookEntryRecord => +const getMockAddressBookEntry = (address: string, name: string = 'test'): AddressBookEntry => makeAddressBookEntry({ address, name, - isOwner, }) +const getMockOldAddressBookEntry = ({ address = '', name = '', isOwner = false }): OldAddressBookEntry => { + return { + address, + name, + isOwner, + } +} + describe('getAddressesListFromAdbk', () => { const entry1 = getMockAddressBookEntry('123456', 'test1') const entry2 = getMockAddressBookEntry('78910', 'test2') @@ -27,11 +33,11 @@ describe('getAddressesListFromAdbk', () => { it('It should returns the list of addresses within the addressBook given a safeAddressBook', () => { // given - const safeAddressBook = List([entry1, entry2, entry3]) + const safeAddressBook = [entry1, entry2, entry3] const expectedResult = [entry1.address, entry2.address, entry3.address] // when - const result = getAddressesListFromSafeAddressBook(safeAddressBook) + const result = getAddressesListFromAddressBook(safeAddressBook) // then expect(result).toStrictEqual(expectedResult) @@ -44,11 +50,11 @@ describe('getNameFromSafeAddressBook', () => { const entry3 = getMockAddressBookEntry('4781321', 'test3') it('It should returns the user name given a safeAddressBook and an user account', () => { // given - const safeAddressBook = List([entry1, entry2, entry3]) + const safeAddressBook = [entry1, entry2, entry3] const expectedResult = entry2.name // when - const result = getNameFromSafeAddressBook(safeAddressBook, entry2.address) + const result = getNameFromAddressBook(safeAddressBook, entry2.address) // then expect(result).toBe(expectedResult) @@ -61,7 +67,7 @@ describe('getOwnersWithNameFromAddressBook', () => { const entry3 = getMockAddressBookEntry('4781321', 'test3') it('It should returns the list of owners with their names given a safeAddressBook and a list of owners', () => { // given - const safeAddressBook = List([entry1, entry2, entry3]) + const safeAddressBook = [entry1, entry2, entry3] const ownerList = List([ { address: entry1.address, name: '' }, { address: entry2.address, name: '' }, @@ -80,14 +86,15 @@ describe('getOwnersWithNameFromAddressBook', () => { }) describe('saveAddressBook', () => { - const safeAddress1 = '0xdfA693da0D16F5E7E78FdCBeDe8FC6eBEa44f1Cf' - const safeAddress2 = '0x344B941b1aAE2e4Be73987212FC4741687Bf0503' - const entry1 = getMockAddressBookEntry('123456', 'test1') - const entry2 = getMockAddressBookEntry('78910', 'test2') - const entry3 = getMockAddressBookEntry('4781321', 'test3') + const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' + const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' + const mockAdd3 = '0x537BD452c3505FC07bA242E437bD29D4E1DC9126' + const entry1 = getMockAddressBookEntry(mockAdd1, 'test1') + const entry2 = getMockAddressBookEntry(mockAdd2, 'test2') + const entry3 = getMockAddressBookEntry(mockAdd3, 'test3') it('It should save a given addressBook to the localStorage', async () => { // given - const addressBook = Map({ [safeAddress1]: List([entry1, entry2]), [safeAddress2]: List([entry3]) }) + const addressBook: AddressBookState = [entry1, entry2, entry3] // when // @ts-ignore @@ -100,3 +107,95 @@ describe('saveAddressBook', () => { expect(result).toStrictEqual(addressBook) }) }) + +describe('migrateOldAddressBook', () => { + const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' + const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' + const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91' + const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8' + + it('It should receive an addressBook in old format and return the same addressBook in new format', () => { + // given + const entry1 = getMockOldAddressBookEntry({ name: 'test1', address: mockAdd1 }) + const entry2 = getMockOldAddressBookEntry({ name: 'test2', address: mockAdd2 }) + + const oldAddressBook: OldAddressBookType = { + [safeAddress1]: [entry1], + [safeAddress2]: [entry2], + } + + const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1') + const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2') + const expectedResult = [expectedEntry1, expectedEntry2] + + // when + const result = migrateOldAddressBook(oldAddressBook) + + // then + expect(result).toStrictEqual(expectedResult) + }) +}) + +jest.mock('src/utils/storage/index') +describe('getAddressBookFromStorage', () => { + const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' + const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' + const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91' + const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8' + afterAll(() => { + jest.unmock('src/utils/storage/index') + }) + it('It should return null if no addressBook in storage', async () => { + // given + + const expectedResult = null + const storageUtils = require('src/utils/storage/index') + const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => null) + + // when + const result = await getAddressBookFromStorage() + + // then + expect(result).toStrictEqual(expectedResult) + expect(spy).toHaveBeenCalled() + }) + it('It should return migrated addressBook if old addressBook in storage', async () => { + // given + const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1') + const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2') + const entry1 = getMockOldAddressBookEntry({ name: 'test1', address: mockAdd1 }) + const entry2 = getMockOldAddressBookEntry({ name: 'test2', address: mockAdd2 }) + const oldAddressBook: OldAddressBookType = { + [safeAddress1]: [entry1], + [safeAddress2]: [entry2], + } + const expectedResult = [expectedEntry1, expectedEntry2] + + const storageUtils = require('src/utils/storage/index') + const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => oldAddressBook) + + // when + const result = await getAddressBookFromStorage() + + // then + expect(result).toStrictEqual(expectedResult) + expect(spy).toHaveBeenCalled() + }) + it('It should return addressBook if addressBook in storage', async () => { + // given + const expectedEntry1 = getMockAddressBookEntry(mockAdd1, 'test1') + const expectedEntry2 = getMockAddressBookEntry(mockAdd2, 'test2') + + const expectedResult = [expectedEntry1, expectedEntry2] + + const storageUtils = require('src/utils/storage/index') + const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => JSON.stringify(expectedResult)) + + // when + const result = await getAddressBookFromStorage() + + // then + expect(result).toStrictEqual(expectedResult) + expect(spy).toHaveBeenCalled() + }) +}) diff --git a/src/logic/addressBook/utils/index.ts b/src/logic/addressBook/utils/index.ts index 8b636892..73743000 100644 --- a/src/logic/addressBook/utils/index.ts +++ b/src/logic/addressBook/utils/index.ts @@ -1,28 +1,62 @@ import { List } from 'immutable' import { loadFromStorage, saveToStorage } from 'src/utils/storage' -import { AddressBookEntryRecord, AddressBookEntryProps } from '../model/addressBook' +import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { SafeOwner } from 'src/logic/safe/store/models/safe' -import { AddressBookCollection } from '../store/reducer/addressBook' -import { AddressBookMap } from '../store/reducer/types/addressBook' +import { sameAddress } from 'src/logic/wallets/ethAddresses' const ADDRESS_BOOK_STORAGE_KEY = 'ADDRESS_BOOK_STORAGE_KEY' -export const getAddressBookFromStorage = async (): Promise | undefined> => { - return await loadFromStorage>(ADDRESS_BOOK_STORAGE_KEY) +export type OldAddressBookEntry = { + address: string + name: string + isOwner: boolean } -export const saveAddressBook = async (addressBook: AddressBookMap): Promise => { +export type OldAddressBookType = { + [safeAddress: string]: [OldAddressBookEntry] +} + +export const migrateOldAddressBook = (oldAddressBook: OldAddressBookType): AddressBookState => { + const values: AddressBookState = [] + const adbkValues = Object.values(oldAddressBook) + + for (const safeIterator of adbkValues) { + for (const safeAddressBook of safeIterator) { + if (!values.find((entry) => sameAddress(entry.address, safeAddressBook.address))) { + values.push(makeAddressBookEntry({ address: safeAddressBook.address, name: safeAddressBook.name })) + } + } + } + + return values +} + +export const getAddressBookFromStorage = async (): Promise => { + const result: OldAddressBookType | string | undefined = await loadFromStorage(ADDRESS_BOOK_STORAGE_KEY) + + if (!result) { + return null + } + + if (typeof result === 'string') { + return JSON.parse(result) + } + + return migrateOldAddressBook(result as OldAddressBookType) +} + +export const saveAddressBook = async (addressBook: AddressBookState): Promise => { try { - await saveToStorage(ADDRESS_BOOK_STORAGE_KEY, addressBook.toJS()) + await saveToStorage(ADDRESS_BOOK_STORAGE_KEY, JSON.stringify(addressBook)) } catch (err) { console.error('Error storing addressBook in localstorage', err) } } -export const getAddressesListFromSafeAddressBook = (addressBook: AddressBookCollection): string[] => - Array.from(addressBook).map((entry: AddressBookEntryRecord) => entry.address) +export const getAddressesListFromAddressBook = (addressBook: AddressBookState): string[] => + addressBook.map((entry) => entry.address) -export const getNameFromSafeAddressBook = (addressBook: AddressBookCollection, userAddress: string): string | null => { +export const getNameFromAddressBook = (addressBook: AddressBookState, userAddress: string): string | null => { const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress) if (entry) { return entry.name @@ -30,15 +64,22 @@ export const getNameFromSafeAddressBook = (addressBook: AddressBookCollection, u return null } +export const getValidAddressBookName = (addressbookName: string): string | null => { + const INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET'] + const isInvalid = INVALID_NAMES.find((invalidName) => addressbookName.toUpperCase().includes(invalidName)) + if (isInvalid) return null + return addressbookName +} + export const getOwnersWithNameFromAddressBook = ( - addressBook: AddressBookCollection, + addressBook: AddressBookState, ownerList: List, -): List | [] => { +): List => { if (!ownerList) { - return [] + return List([]) } return ownerList.map((owner) => { - const ownerName = getNameFromSafeAddressBook(addressBook, owner.address) + const ownerName = getNameFromAddressBook(addressBook, owner.address) return { address: owner.address, name: ownerName || owner.name, diff --git a/src/logic/safe/store/actions/addSafe.ts b/src/logic/safe/store/actions/addSafe.ts index 0317a22f..90851dab 100644 --- a/src/logic/safe/store/actions/addSafe.ts +++ b/src/logic/safe/store/actions/addSafe.ts @@ -18,15 +18,16 @@ export const buildOwnersFrom = (names: string[], addresses: string[]): List ({ +export const addSafe = createAction(ADD_SAFE, (safe: SafeRecordProps, loadedFromStorage = false) => ({ safe, + loadedFromStorage, })) const saveSafe = (safe: SafeRecordProps) => (dispatch: Dispatch, getState: () => AppReduxState): void => { const state = getState() const safeList = safesListSelector(state) - dispatch(addSafe(safe)) + dispatch(addSafe(safe, true)) if (safeList.size === 0) { dispatch(setDefaultSafe(safe.address)) diff --git a/src/logic/safe/store/actions/loadSafesFromStorage.ts b/src/logic/safe/store/actions/loadSafesFromStorage.ts index de752b0e..c6009857 100644 --- a/src/logic/safe/store/actions/loadSafesFromStorage.ts +++ b/src/logic/safe/store/actions/loadSafesFromStorage.ts @@ -13,7 +13,7 @@ const loadSafesFromStorage = () => async (dispatch: Dispatch): Promise => if (safes) { Object.values(safes).forEach((safeProps) => { - dispatch(addSafe(buildSafe(safeProps))) + dispatch(addSafe(buildSafe(safeProps), true)) }) } } catch (err) { diff --git a/src/logic/safe/store/middleware/safeStorage.ts b/src/logic/safe/store/middleware/safeStorage.ts index e5b2feef..4f588d0b 100644 --- a/src/logic/safe/store/middleware/safeStorage.ts +++ b/src/logic/safe/store/middleware/safeStorage.ts @@ -1,4 +1,3 @@ -import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { addAddressBookEntry } from 'src/logic/addressBook/store/actions/addAddressBookEntry' import { saveDefaultSafe, saveSafes } from 'src/logic/safe/utils' import { tokensSelector } from 'src/logic/tokens/store/selectors' @@ -13,6 +12,12 @@ import { REPLACE_SAFE_OWNER } from 'src/logic/safe/store/actions/replaceSafeOwne import { SET_DEFAULT_SAFE } from 'src/logic/safe/store/actions/setDefaultSafe' import { UPDATE_SAFE } from 'src/logic/safe/store/actions/updateSafe' import { getActiveTokensAddressesForAllSafes, safesMapSelector } from 'src/logic/safe/store/selectors' +import { checksumAddress } from 'src/utils/checksumAddress' +import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' +import { getValidAddressBookName } from 'src/logic/addressBook/utils' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' +import { sameAddress } from 'src/logic/wallets/ethAddresses' const watchedActions = [ ADD_SAFE, @@ -41,6 +46,30 @@ const recalculateActiveTokens = (state) => { saveActiveTokens(activeTokens) } +/** + * If the owner has a valid name that means that should be on the addressBook + * if the owner is not currently on the addressBook, that means the user deleted it + * or that it's a new safe with valid names, so we also check if it's a new safe or an already loaded one + * @param name + * @param address + * @param addressBook + * @param safeAlreadyLoaded -> true if the safe was loaded from the localStorage + */ +// TODO TEST +const checkIfOwnerWasDeletedFromAddressBook = ( + { name, address }: AddressBookEntry, + addressBook: AddressBookState, + safeAlreadyLoaded: boolean, +) => { + if (!safeAlreadyLoaded) { + return false + } + + const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name) + const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address)) + return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook +} + const safeStorageMware = (store) => (next) => async (action) => { const handledAction = next(action) @@ -48,6 +77,7 @@ const safeStorageMware = (store) => (next) => async (action) => { const state = store.getState() const { dispatch } = store const safes = safesMapSelector(state) + const addressBook = addressBookSelector(state) await saveSafes(safes.toJSON()) switch (action.type) { @@ -56,19 +86,42 @@ const safeStorageMware = (store) => (next) => async (action) => { break } case ADD_SAFE: { - const { safe } = action.payload - const ownersArray = safe.owners.toJS() - // Adds the owners to the address book - ownersArray.forEach((owner) => { - dispatch(addAddressBookEntry(makeAddressBookEntry({ ...owner, isOwner: true }))) + const { safe, loadedFromStorage } = action.payload + safe.owners.forEach((owner) => { + const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name }) + const ownerWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook( + checksumEntry, + addressBook, + loadedFromStorage, + ) + + if (!ownerWasAlreadyInAddressBook) { + dispatch(addAddressBookEntry(checksumEntry, { notifyEntryUpdate: false })) + } }) + const safeWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook( + { address: safe.address, name: safe.name }, + addressBook, + loadedFromStorage, + ) + + if (!safeWasAlreadyInAddressBook) { + dispatch( + addAddressBookEntry(makeAddressBookEntry({ address: safe.address, name: safe.name }), { + notifyEntryUpdate: true, + }), + ) + } break } case UPDATE_SAFE: { - const { activeTokens } = action.payload + const { activeTokens, name, address } = action.payload if (activeTokens) { recalculateActiveTokens(state) } + if (name) { + dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })), { notifyEntryUpdate: false }) + } break } case SET_DEFAULT_SAFE: { diff --git a/src/logic/safe/store/reducer/allTransactions.ts b/src/logic/safe/store/reducer/allTransactions.ts index 4e95bb7d..48445649 100644 --- a/src/logic/safe/store/reducer/allTransactions.ts +++ b/src/logic/safe/store/reducer/allTransactions.ts @@ -1,7 +1,10 @@ import { handleActions } from 'redux-actions' -import { Transaction } from '../models/types/transactions' -import { LOAD_MORE_TRANSACTIONS, LoadMoreTransactionsAction } from '../actions/allTransactions/pagination' +import { Transaction } from 'src/logic/safe/store/models/types/transactions' +import { + LOAD_MORE_TRANSACTIONS, + LoadMoreTransactionsAction, +} from 'src/logic/safe/store/actions/allTransactions/pagination' export const TRANSACTIONS = 'allTransactions' diff --git a/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx b/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx index 628a7af0..c8c5073a 100644 --- a/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx +++ b/src/routes/safe/components/AddressBook/CreateEditEntryModal/index.tsx @@ -19,8 +19,8 @@ import Col from 'src/components/layout/Col' import Hairline from 'src/components/layout/Hairline' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' -import { getAddressBook } from 'src/logic/addressBook/store/selectors' -import { getAddressesListFromSafeAddressBook } from 'src/logic/addressBook/utils' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' +import { getAddressesListFromAddressBook } from 'src/logic/addressBook/utils' export const CREATE_ENTRY_INPUT_NAME_ID = 'create-entry-input-name' export const CREATE_ENTRY_INPUT_ADDRESS_ID = 'create-entry-input-address' @@ -42,8 +42,8 @@ const CreateEditEntryModalComponent = ({ } } - const addressBook = useSelector(getAddressBook) - const addressBookAddressesList = getAddressesListFromSafeAddressBook(addressBook) + const addressBook = useSelector(addressBookSelector) + const addressBookAddressesList = getAddressesListFromAddressBook(addressBook) const entryDoesntExist = uniqueAddress(addressBookAddressesList) const formMutators = { diff --git a/src/routes/safe/components/AddressBook/columns.ts b/src/routes/safe/components/AddressBook/columns.ts index 6548e18a..79ddf531 100644 --- a/src/routes/safe/components/AddressBook/columns.ts +++ b/src/routes/safe/components/AddressBook/columns.ts @@ -1,4 +1,5 @@ import { List } from 'immutable' +import { TableCellProps } from '@material-ui/core/TableCell/TableCell' export const ADDRESS_BOOK_ROW_ID = 'address-book-row' export const TX_TABLE_ADDRESS_BOOK_ID = 'idAddressBook' @@ -9,7 +10,17 @@ export const EDIT_ENTRY_BUTTON = 'edit-entry-btn' export const REMOVE_ENTRY_BUTTON = 'remove-entry-btn' export const SEND_ENTRY_BUTTON = 'send-entry-btn' -export const generateColumns = () => { +type AddressBookColumn = { + id: string + order: boolean + disablePadding?: boolean + label: string + width?: number + custom?: boolean + align?: TableCellProps['align'] +} + +export const generateColumns = (): List => { const nameColumn = { id: AB_NAME_ID, order: false, diff --git a/src/routes/safe/components/AddressBook/index.tsx b/src/routes/safe/components/AddressBook/index.tsx index 9362b09e..42597e64 100644 --- a/src/routes/safe/components/AddressBook/index.tsx +++ b/src/routes/safe/components/AddressBook/index.tsx @@ -1,10 +1,8 @@ import TableCell from '@material-ui/core/TableCell' import TableContainer from '@material-ui/core/TableContainer' import TableRow from '@material-ui/core/TableRow' -import { withStyles } from '@material-ui/core/styles' -// import CallMade from '@material-ui/icons/CallMade' +import { makeStyles } from '@material-ui/core/styles' import cn from 'classnames' -// import classNames from 'classnames/bind' import React, { useEffect, useState } from 'react' import { useDispatch, useSelector } from 'react-redux' @@ -18,11 +16,11 @@ import ButtonLink from 'src/components/layout/ButtonLink' import Col from 'src/components/layout/Col' import Img from 'src/components/layout/Img' import Row from 'src/components/layout/Row' -import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { AddressBookEntry, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { addAddressBookEntry } from 'src/logic/addressBook/store/actions/addAddressBookEntry' import { removeAddressBookEntry } from 'src/logic/addressBook/store/actions/removeAddressBookEntry' import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' -import { getAddressBook } from 'src/logic/addressBook/store/selectors' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { isUserAnOwnerOfAnySafe } from 'src/logic/wallets/ethAddresses' import CreateEditEntryModal from 'src/routes/safe/components/AddressBook/CreateEditEntryModal' import DeleteEntryModal from 'src/routes/safe/components/AddressBook/DeleteEntryModal' @@ -38,19 +36,32 @@ import SendModal from 'src/routes/safe/components/Balances/SendModal' import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' import RenameOwnerIcon from 'src/routes/safe/components/Settings/ManageOwners/assets/icons/rename-owner.svg' import RemoveOwnerIcon from 'src/routes/safe/components/Settings/assets/icons/bin.svg' -import RemoveOwnerIconDisabled from 'src/routes/safe/components/Settings/assets/icons/disabled-bin.svg' import { addressBookQueryParamsSelector, safesListSelector } from 'src/logic/safe/store/selectors' import { checksumAddress } from 'src/utils/checksumAddress' +import { grantedSelector } from 'src/routes/safe/container/selector' import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics' +import { getValidAddressBookName } from 'src/logic/addressBook/utils' -const AddressBookTable = ({ classes }) => { +const useStyles = makeStyles(styles) + +interface AddressBookSelectedEntry extends AddressBookEntry { + isNew?: boolean +} + +const AddressBookTable = (): React.ReactElement => { + const classes = useStyles() const columns = generateColumns() const autoColumns = columns.filter((c) => !c.custom) const dispatch = useDispatch() const safesList = useSelector(safesListSelector) const entryAddressToEditOrCreateNew = useSelector(addressBookQueryParamsSelector) - const addressBook = useSelector(getAddressBook) - const [selectedEntry, setSelectedEntry] = useState(null) + const addressBook = useSelector(addressBookSelector) + const granted = useSelector(grantedSelector) + const [selectedEntry, setSelectedEntry] = useState<{ + entry?: AddressBookSelectedEntry + index?: number + isOwnerAddress?: boolean + } | null>(null) const [editCreateEntryModalOpen, setEditCreateEntryModalOpen] = useState(false) const [deleteEntryModalOpen, setDeleteEntryModalOpen] = useState(false) const [sendFundsModalOpen, setSendFundsModalOpen] = useState(false) @@ -69,11 +80,10 @@ const AddressBookTable = ({ classes }) => { useEffect(() => { if (entryAddressToEditOrCreateNew) { const checksumEntryAdd = checksumAddress(entryAddressToEditOrCreateNew) - const key = addressBook.findKey((entry) => entry.address === checksumEntryAdd) - if (key && key >= 0) { + const oldEntryIndex = addressBook.findIndex((entry) => entry.address === checksumEntryAdd) + if (oldEntryIndex >= 0) { // Edit old entry - const value = addressBook.get(key) - setSelectedEntry({ entry: value, index: key }) + setSelectedEntry({ entry: addressBook[oldEntryIndex], index: oldEntryIndex }) } else { // Create new entry setSelectedEntry({ @@ -107,7 +117,7 @@ const AddressBookTable = ({ classes }) => { } const deleteEntryModalHandler = () => { - const entryAddress = checksumAddress(selectedEntry.entry.address) + const entryAddress = selectedEntry && selectedEntry.entry ? checksumAddress(selectedEntry.entry.address) : '' setSelectedEntry(null) setDeleteEntryModalOpen(false) dispatch(removeAddressBookEntry(entryAddress)) @@ -138,7 +148,7 @@ const AddressBookTable = ({ classes }) => { defaultRowsPerPage={25} disableLoadingOnEmptyTable label="Owners" - size={addressBook?.size || 0} + size={addressBook?.length || 0} > {(sortedData) => sortedData.map((row, index) => { @@ -151,20 +161,22 @@ const AddressBookTable = ({ classes }) => { key={index} tabIndex={-1} > - {autoColumns.map((column: any) => ( - - {column.id === AB_ADDRESS_ID ? ( - - ) : ( - row[column.id] - )} - - ))} + {autoColumns.map((column) => { + return ( + + {column.id === AB_ADDRESS_ID ? ( + + ) : ( + getValidAddressBookName(row[column.id]) + )} + + ) + })} Edit entry { setSelectedEntry({ entry: row, @@ -177,33 +189,29 @@ const AddressBookTable = ({ classes }) => { /> Remove entry { - if (!userOwner) { - setSelectedEntry({ entry: row }) - setDeleteEntryModalOpen(true) - } - }} - src={userOwner ? RemoveOwnerIconDisabled : RemoveOwnerIcon} - testId={REMOVE_ENTRY_BUTTON} - /> - + src={RemoveOwnerIcon} + testId={REMOVE_ENTRY_BUTTON} + /> + {granted ? ( + + ) : null} @@ -236,4 +244,4 @@ const AddressBookTable = ({ classes }) => { ) } -export default withStyles(styles as any)(AddressBookTable) +export default AddressBookTable diff --git a/src/routes/safe/components/AddressBook/style.ts b/src/routes/safe/components/AddressBook/style.ts index b676dd63..f0e6c980 100644 --- a/src/routes/safe/components/AddressBook/style.ts +++ b/src/routes/safe/components/AddressBook/style.ts @@ -1,6 +1,7 @@ import { lg, marginButtonImg, md, sm } from 'src/theme/variables' +import { createStyles } from '@material-ui/core' -export const styles = () => ({ +export const styles = createStyles({ formContainer: { minHeight: '250px', }, @@ -38,6 +39,9 @@ export const styles = () => ({ cursor: 'pointer', marginBottom: marginButtonImg, }, + editEntryButtonNonOwner: { + cursor: 'pointer', + }, removeEntryButton: { marginLeft: lg, marginRight: lg, @@ -50,6 +54,11 @@ export const styles = () => ({ marginBottom: marginButtonImg, cursor: 'default', }, + removeEntryButtonNonOwner: { + marginLeft: lg, + marginRight: lg, + cursor: 'pointer', + }, message: { padding: `${md} 0`, maxHeight: '54px', diff --git a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx index ddd0791d..f766ea53 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx @@ -1,7 +1,6 @@ import MuiTextField from '@material-ui/core/TextField' import makeStyles from '@material-ui/core/styles/makeStyles' import Autocomplete from '@material-ui/lab/Autocomplete' -import { List } from 'immutable' import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' import { trimSpaces } from 'src/utils/strings' @@ -10,10 +9,10 @@ import { styles } from './style' import Identicon from 'src/components/Identicon' import { mustBeEthereumAddress, mustBeEthereumContractAddress } from 'src/components/forms/validator' -import { getAddressBook } from 'src/logic/addressBook/store/selectors' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { getAddressFromENS } from 'src/logic/wallets/getWeb3' import { isValidEnsName } from 'src/logic/wallets/ethAddresses' -import { AddressBookEntryRecord } from 'src/logic/addressBook/model/addressBook' +import { AddressBookEntry, AddressBookState } from 'src/logic/addressBook/model/addressBook' export interface AddressBookProps { fieldMutator: (address: string) => void @@ -44,12 +43,10 @@ const textFieldInputStyle = makeStyles(() => ({ }, })) -const filterAddressBookWithContractAddresses = async ( - addressBook: List, -): Promise> => { +const filterAddressBookWithContractAddresses = async (addressBook: AddressBookState): Promise => { const abFlags = await Promise.all( addressBook.map( - async ({ address }: AddressBookEntryRecord): Promise => { + async ({ address }: AddressBookEntry): Promise => { return (await mustBeEthereumContractAddress(address)) === undefined }, ), @@ -58,11 +55,6 @@ const filterAddressBookWithContractAddresses = async ( return addressBook.filter((_, index) => abFlags[index]) } -interface FilteredAddressBookEntry { - name: string - address: string -} - const AddressBookInput = ({ fieldMutator, isCustomTx, @@ -72,12 +64,12 @@ const AddressBookInput = ({ setSelectedEntry, }: AddressBookProps): React.ReactElement => { const classes = useStyles() - const addressBook = useSelector(getAddressBook) + const addressBook = useSelector(addressBookSelector) const [isValidForm, setIsValidForm] = useState(true) const [validationText, setValidationText] = useState('') const [inputTouched, setInputTouched] = useState(false) const [blurred, setBlurred] = useState(pristine) - const [adbkList, setADBKList] = useState>(List([])) + const [adbkList, setADBKList] = useState([]) const [inputAddValue, setInputAddValue] = useState(recipientAddress) @@ -168,7 +160,7 @@ const AddressBookInput = ({ freeSolo getOptionLabel={(adbkEntry) => adbkEntry.address || ''} id="free-solo-demo" - onChange={(_, value: FilteredAddressBookEntry) => { + onChange={(_, value: AddressBookEntry) => { let address = '' let name = '' if (value) { @@ -184,7 +176,7 @@ const AddressBookInput = ({ setBlurred(false) }} open={!blurred} - options={adbkList.toArray()} + options={adbkList} renderInput={(params) => ( { +const SendCollectible = ({ + initialValues, + onClose, + onNext, + recipientAddress, + selectedToken = {}, +}): React.ReactElement => { const classes = useStyles() const nftAssets = useSelector(safeActiveSelectorMap) const nftTokens = useSelector(nftTokensSelector) - const addressBook = useSelector(getAddressBook) - const [selectedEntry, setSelectedEntry] = useState({ + const addressBook = useSelector(addressBookSelector) + const [selectedEntry, setSelectedEntry] = useState<{ address?: string; name?: string | null } | null>({ address: recipientAddress || initialValues.recipientAddress, name: '', }) @@ -64,7 +70,7 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel const handleSubmit = (values) => { // If the input wasn't modified, there was no mutation of the recipientAddress if (!values.recipientAddress) { - values.recipientAddress = selectedEntry.address + values.recipientAddress = selectedEntry?.address } values.assetName = nftAssets[values.assetAddress].name @@ -97,10 +103,10 @@ const SendCollectible = ({ initialValues, onClose, onNext, recipientAddress, sel if (scannedAddress.startsWith('ethereum:')) { scannedAddress = scannedAddress.replace('ethereum:', '') } - const scannedName = addressBook ? getNameFromSafeAddressBook(addressBook, scannedAddress) : '' + const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : '' mutators.setRecipient(scannedAddress) setSelectedEntry({ - name: scannedName || '', + name: scannedName, address: scannedAddress, }) closeQrModal() diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/style.ts b/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/style.ts index 44d7d9bb..776dea04 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/style.ts +++ b/src/routes/safe/components/Balances/SendModal/screens/SendCollectible/style.ts @@ -1,6 +1,7 @@ import { lg, md, secondaryText } from 'src/theme/variables' +import { createStyles } from '@material-ui/core' -export const styles = () => ({ +export const styles = createStyles({ heading: { padding: `${md} ${lg}`, justifyContent: 'flex-start', diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.tsx index 11b293d8..27db52e9 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.tsx @@ -25,8 +25,8 @@ import Col from 'src/components/layout/Col' import Hairline from 'src/components/layout/Hairline' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' -import { getAddressBook } from 'src/logic/addressBook/store/selectors' -import { getNameFromSafeAddressBook } from 'src/logic/addressBook/utils' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBook } from 'src/logic/addressBook/utils' import SafeInfo from 'src/routes/safe/components/Balances/SendModal/SafeInfo' import AddressBookInput from 'src/routes/safe/components/Balances/SendModal/screens/AddressBookInput' @@ -69,8 +69,8 @@ const SendFunds = ({ }: SendFundsProps): React.ReactElement => { const classes = useStyles() const tokens = useSelector(extendedSafeTokensSelector) - const addressBook = useSelector(getAddressBook) - const [selectedEntry, setSelectedEntry] = useState({ + const addressBook = useSelector(addressBookSelector) + const [selectedEntry, setSelectedEntry] = useState<{ address?: string; name?: string | null } | null>({ address: recipientAddress || initialValues.recipientAddress, name: '', }) @@ -88,7 +88,7 @@ const SendFunds = ({ const submitValues = values // If the input wasn't modified, there was no mutation of the recipientAddress if (!values.recipientAddress) { - submitValues.recipientAddress = selectedEntry.address + submitValues.recipientAddress = selectedEntry?.address } onNext(submitValues) } @@ -118,7 +118,7 @@ const SendFunds = ({ if (scannedAddress.startsWith('ethereum:')) { scannedAddress = scannedAddress.replace('ethereum:', '') } - const scannedName = addressBook ? getNameFromSafeAddressBook(addressBook, scannedAddress) : '' + const scannedName = addressBook ? getNameFromAddressBook(addressBook, scannedAddress) : '' mutators.setRecipient(scannedAddress) setSelectedEntry({ name: scannedName || '', diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx index caac7551..7f6fbec5 100644 --- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.tsx @@ -20,12 +20,12 @@ import Hairline from 'src/components/layout/Hairline' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' -import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' import { NOTIFICATIONS } from 'src/logic/notifications' import editSafeOwner from 'src/logic/safe/store/actions/editSafeOwner' import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' import { sm } from 'src/theme/variables' import enqueueSnackbar from 'src/logic/notifications/store/actions/enqueueSnackbar' +import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' export const RENAME_OWNER_INPUT_TEST_ID = 'rename-owner-input' export const SAVE_OWNER_CHANGES_BTN_TEST_ID = 'save-owner-changes-btn' @@ -47,7 +47,7 @@ const EditOwnerComponent = ({ isOpen, onClose, ownerAddress, selectedOwnerName } const { ownerName } = values dispatch(editSafeOwner({ safeAddress, ownerAddress, ownerName })) - dispatch(updateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName }))) + dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: ownerAddress, name: ownerName }))) dispatch(enqueueSnackbar(NOTIFICATIONS.OWNER_NAME_CHANGE_EXECUTED_MSG)) onClose() diff --git a/src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell/index.tsx b/src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell/index.tsx index 5cb4a402..13313a54 100644 --- a/src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell/index.tsx @@ -6,6 +6,7 @@ import Block from 'src/components/layout/Block' import Paragraph from 'src/components/layout/Paragraph' import { useWindowDimensions } from 'src/logic/hooks/useWindowDimensions' import { useEffect, useState } from 'react' +import { getValidAddressBookName } from 'src/logic/addressBook/utils' type OwnerAddressTableCellProps = { address: string @@ -34,7 +35,7 @@ const OwnerAddressTableCell = (props: OwnerAddressTableCellProps): React.ReactEl {showLinks ? (
- {!userName || userName === 'UNKNOWN' ? null : userName} + {userName && getValidAddressBookName(userName)}
) : ( diff --git a/src/routes/safe/components/Settings/ManageOwners/index.tsx b/src/routes/safe/components/Settings/ManageOwners/index.tsx index 446a5c0f..1293ea4e 100644 --- a/src/routes/safe/components/Settings/ManageOwners/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/index.tsx @@ -30,8 +30,8 @@ import Paragraph from 'src/components/layout/Paragraph/index' import Row from 'src/components/layout/Row' import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils' import { useAnalytics, SAFE_NAVIGATION_EVENT } from 'src/utils/googleAnalytics' +import { AddressBookState } from 'src/logic/addressBook/model/addressBook' import { SafeOwner } from 'src/logic/safe/store/models/safe' -import { AddressBookCollection } from 'src/logic/addressBook/store/reducer/addressBook' export const RENAME_OWNER_BTN_TEST_ID = 'rename-owner-btn' export const REMOVE_OWNER_BTN_TEST_ID = 'remove-owner-btn' @@ -42,7 +42,7 @@ export const OWNERS_ROW_TEST_ID = 'owners-row' const useStyles = makeStyles(styles) type Props = { - addressBook: unknown + addressBook: AddressBookState granted: boolean owners: List } @@ -84,7 +84,7 @@ const ManageOwners = ({ addressBook, granted, owners }: Props): React.ReactEleme const columns = generateColumns() const autoColumns = columns.filter((c) => !c.custom) - const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook as AddressBookCollection, owners) + const ownersAdbk = getOwnersWithNameFromAddressBook(addressBook, owners) const ownerData = getOwnerData(ownersAdbk) return ( diff --git a/src/routes/safe/components/Settings/index.tsx b/src/routes/safe/components/Settings/index.tsx index 50fdb843..bf7281aa 100644 --- a/src/routes/safe/components/Settings/index.tsx +++ b/src/routes/safe/components/Settings/index.tsx @@ -23,7 +23,7 @@ import Img from 'src/components/layout/Img' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import Span from 'src/components/layout/Span' -import { getAddressBook } from 'src/logic/addressBook/store/selectors' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { grantedSelector } from 'src/routes/safe/container/selector' import { safeNeedsUpdateSelector, safeOwnersSelector } from 'src/logic/safe/store/selectors' @@ -42,7 +42,7 @@ const Settings: React.FC = () => { const owners = useSelector(safeOwnersSelector) const needsUpdate = useSelector(safeNeedsUpdateSelector) const granted = useSelector(grantedSelector) - const addressBook = useSelector(getAddressBook) + const addressBook = useSelector(addressBookSelector) const handleChange = (menuOptionIndex) => () => { setState((prevState) => ({ ...prevState, menuOptionIndex })) diff --git a/src/store/index.ts b/src/store/index.ts index cd67c3f6..0b5d7a8d 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -6,7 +6,6 @@ import thunk from 'redux-thunk' import addressBookMiddleware from 'src/logic/addressBook/store/middleware/addressBookMiddleware' import addressBook, { ADDRESS_BOOK_REDUCER_ID } from 'src/logic/addressBook/store/reducer/addressBook' -import { AddressBookReducerMap } from 'src/logic/addressBook/store/reducer/types/addressBook.d' import { NFT_ASSETS_REDUCER_ID, NFT_TOKENS_REDUCER_ID, @@ -19,8 +18,10 @@ import currencyValues, { CURRENCY_VALUES_KEY, CurrencyValuesState, } from 'src/logic/currencyValues/store/reducer/currencyValues' -import { CurrentSessionState } from 'src/logic/currentSession/store/reducer/currentSession' -import currentSession, { CURRENT_SESSION_REDUCER_ID } from 'src/logic/currentSession/store/reducer/currentSession' +import currentSession, { + CURRENT_SESSION_REDUCER_ID, + CurrentSessionState, +} from 'src/logic/currentSession/store/reducer/currentSession' import notifications, { NOTIFICATIONS_REDUCER_ID } from 'src/logic/notifications/store/reducer/notifications' import tokens, { TOKEN_REDUCER_ID, TokenState } from 'src/logic/tokens/store/reducer/tokens' import providerWatcher from 'src/logic/wallets/store/middlewares/providerWatcher' @@ -38,7 +39,8 @@ import safe, { SAFE_REDUCER_ID } from 'src/logic/safe/store/reducer/safe' import transactions, { TRANSACTIONS_REDUCER_ID } from 'src/logic/safe/store/reducer/transactions' import { NFTAssets, NFTTokens } from 'src/logic/collectibles/sources/OpenSea' import { SafeReducerMap } from 'src/routes/safe/store/reducer/types/safe' -import allTransactions, { TRANSACTIONS, TransactionsState } from 'src/logic/safe/store/reducer/allTransactions' +import allTransactions, { TRANSACTIONS, TransactionsState } from '../logic/safe/store/reducer/allTransactions' +import { AddressBookState } from 'src/logic/addressBook/model/addressBook' export const history = createHashHistory() @@ -86,7 +88,7 @@ export type AppReduxState = CombinedState<{ [NOTIFICATIONS_REDUCER_ID]: Map [CURRENCY_VALUES_KEY]: CurrencyValuesState [COOKIES_REDUCER_ID]: Map - [ADDRESS_BOOK_REDUCER_ID]: AddressBookReducerMap + [ADDRESS_BOOK_REDUCER_ID]: AddressBookState [CURRENT_SESSION_REDUCER_ID]: CurrentSessionState [TRANSACTIONS]: TransactionsState router: RouterState From eebe972bac251d03f3ae1966722d1c8d9f18c448 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Tue, 22 Sep 2020 22:01:55 +0400 Subject: [PATCH 05/11] Feature: Send env info message on safe apps sdk initialization (#1349) --- .../safe/store/actions/createTransaction.ts | 29 +++++-------------- .../safe/store/actions/processTransaction.ts | 2 +- .../middleware/notificationsMiddleware.ts | 2 +- .../offchainSigner/EIP712Signer.ts | 10 ++++--- .../transactions/offchainSigner/ethSigner.ts | 27 +++++------------ .../safe/transactions/offchainSigner/index.ts | 6 ++-- .../components/ConfirmTransactionModal.tsx | 18 +++++++----- .../Apps/hooks/useIframeMessageHandler.ts | 12 ++++++-- src/routes/safe/components/Apps/index.tsx | 5 ++-- 9 files changed, 48 insertions(+), 63 deletions(-) diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index 76ded3a5..95ea40e5 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -111,7 +111,7 @@ interface CreateTransactionArgs { type CreateTransactionAction = ThunkAction, AppReduxState, undefined, AnyAction> type ConfirmEventHandler = (safeTxHash: string) => void -type RejectEventHandler = () => void +type ErrorEventHandler = () => void const createTransaction = ( { @@ -126,7 +126,7 @@ const createTransaction = ( origin = null, }: CreateTransactionArgs, onUserConfirm?: ConfirmEventHandler, - onUserReject?: RejectEventHandler, + onError?: ErrorEventHandler, ): CreateTransactionAction => async (dispatch: Dispatch, getState: () => AppReduxState): Promise => { const state = getState() @@ -172,6 +172,7 @@ const createTransaction = ( sender: from, sigs, } + const safeTxHash = generateSafeTxHash(safeAddress, txArgs) try { // Here we're checking that safe contract version is greater or equal 1.1.1, but @@ -179,20 +180,19 @@ const createTransaction = ( const canTryOffchainSigning = !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) if (canTryOffchainSigning) { - const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet) + const signature = await tryOffchainSigning(safeTxHash, { ...txArgs, safeAddress }, hardwareWallet) if (signature) { dispatch(closeSnackbarAction({ key: beforeExecutionKey })) + dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded)) + dispatch(fetchTransactions(safeAddress)) await saveTxToHistory({ ...txArgs, signature, origin }) - dispatch(enqueueSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded)) - - dispatch(fetchTransactions(safeAddress)) + onUserConfirm?.(safeTxHash) return } } - const safeTxHash = generateSafeTxHash(safeAddress, txArgs) const tx = isExecution ? await getExecutionTransaction(txArgs) : await getApprovalTransaction(safeInstance, safeTxHash) @@ -245,20 +245,7 @@ const createTransaction = ( removeTxFromStore(mockedTx, safeAddress, dispatch, state) console.error('Tx error: ', error) - // Different wallets return different error messages in this case. This is an assumption that if - // error message includes "user" word, the tx was rejected by user - - let errorIncludesUserWord = false - if (typeof error === 'string') { - errorIncludesUserWord = (error as string).includes('User') || (error as string).includes('user') - } - if (error.message) { - errorIncludesUserWord = error.message.includes('User') || error.message.includes('user') - } - - if (errorIncludesUserWord) { - onUserReject?.() - } + onError?.() }) .then(async (receipt) => { if (pendingExecutionKey) { diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts index ffe0b2d2..a4e2aa6e 100644 --- a/src/logic/safe/store/actions/processTransaction.ts +++ b/src/logic/safe/store/actions/processTransaction.ts @@ -78,7 +78,7 @@ const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddres const canTryOffchainSigning = !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) if (canTryOffchainSigning) { - const signature = await tryOffchainSigning({ ...txArgs, safeAddress }, hardwareWallet) + const signature = await tryOffchainSigning(tx.safeTxHash, { ...txArgs, safeAddress }, hardwareWallet) if (signature) { dispatch(closeSnackbarAction(beforeExecutionKey)) diff --git a/src/logic/safe/store/middleware/notificationsMiddleware.ts b/src/logic/safe/store/middleware/notificationsMiddleware.ts index a9fbf648..617bdd3d 100644 --- a/src/logic/safe/store/middleware/notificationsMiddleware.ts +++ b/src/logic/safe/store/middleware/notificationsMiddleware.ts @@ -103,7 +103,7 @@ const notificationsMiddleware = (store) => (next) => async (action) => { } case ADD_INCOMING_TRANSACTIONS: { action.payload.forEach((incomingTransactions, safeAddress) => { - const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress) + const { latestIncomingTxBlock } = state.safes.get('safes').get(safeAddress, {}) const viewedSafes = state.currentSession['viewedSafes'] const recurringUser = viewedSafes?.includes(safeAddress) diff --git a/src/logic/safe/transactions/offchainSigner/EIP712Signer.ts b/src/logic/safe/transactions/offchainSigner/EIP712Signer.ts index 9b77ae2f..55fe61d7 100644 --- a/src/logic/safe/transactions/offchainSigner/EIP712Signer.ts +++ b/src/logic/safe/transactions/offchainSigner/EIP712Signer.ts @@ -1,5 +1,6 @@ -import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' +import { AbstractProvider } from 'web3-core' import { getWeb3 } from 'src/logic/wallets/getWeb3' +import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' const EIP712_NOT_SUPPORTED_ERROR_MSG = "EIP712 is not supported by user's wallet" @@ -59,7 +60,7 @@ const generateTypedDataFrom = async ({ } export const getEIP712Signer = (version?: string) => async (txArgs) => { - const web3: any = getWeb3() + const web3 = getWeb3() const typedData = await generateTypedDataFrom(txArgs) let method = 'eth_signTypedData_v3' @@ -80,13 +81,14 @@ export const getEIP712Signer = (version?: string) => async (txArgs) => { } return new Promise((resolve, reject) => { - web3.currentProvider.sendAsync(signedTypedData, (err, signature) => { + const provider = web3.currentProvider as AbstractProvider + provider.sendAsync(signedTypedData, (err, signature) => { if (err) { reject(err) return } - if (signature.result == null) { + if (signature?.result == null) { reject(new Error(EIP712_NOT_SUPPORTED_ERROR_MSG)) return } diff --git a/src/logic/safe/transactions/offchainSigner/ethSigner.ts b/src/logic/safe/transactions/offchainSigner/ethSigner.ts index 08f0dc86..5daeedba 100644 --- a/src/logic/safe/transactions/offchainSigner/ethSigner.ts +++ b/src/logic/safe/transactions/offchainSigner/ethSigner.ts @@ -4,26 +4,13 @@ import { AbstractProvider } from 'web3-core/types' const ETH_SIGN_NOT_SUPPORTED_ERROR_MSG = 'ETH_SIGN_NOT_SUPPORTED' -export const ethSigner = async ({ - baseGas, - data, - gasPrice, - gasToken, - nonce, - operation, - refundReceiver, - safeInstance, - safeTxGas, - sender, - to, - valueInWei, -}): Promise => { +type EthSignerArgs = { + safeTxHash: string + sender: string +} + +export const ethSigner = async ({ safeTxHash, sender }: EthSignerArgs): Promise => { const web3 = await getWeb3() - const txHash = await safeInstance.methods - .getTransactionHash(to, valueInWei, data, operation, safeTxGas, baseGas, gasPrice, gasToken, refundReceiver, nonce) - .call({ - from: sender, - }) return new Promise(function (resolve, reject) { const provider = web3.currentProvider as AbstractProvider @@ -31,7 +18,7 @@ export const ethSigner = async ({ { jsonrpc: '2.0', method: 'eth_sign', - params: [sender, txHash], + params: [sender, safeTxHash], id: new Date().getTime(), }, async function (err, signature) { diff --git a/src/logic/safe/transactions/offchainSigner/index.ts b/src/logic/safe/transactions/offchainSigner/index.ts index 047c04d7..6113d568 100644 --- a/src/logic/safe/transactions/offchainSigner/index.ts +++ b/src/logic/safe/transactions/offchainSigner/index.ts @@ -8,7 +8,7 @@ import { ethSigner } from './ethSigner' const SIGNERS = { EIP712_V3: getEIP712Signer('v3'), EIP712_V4: getEIP712Signer('v4'), - EIP712: getEIP712Signer() as any, + EIP712: getEIP712Signer(), ETH_SIGN: ethSigner, } @@ -18,13 +18,13 @@ const getSignersByWallet = (isHW) => export const SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES = '>=1.1.1' -export const tryOffchainSigning = async (txArgs, isHW) => { +export const tryOffchainSigning = async (safeTxHash: string, txArgs, isHW: boolean): Promise => { let signature const signerByWallet = getSignersByWallet(isHW) for (const signingFunc of signerByWallet) { try { - signature = await signingFunc(txArgs) + signature = await signingFunc({ ...txArgs, safeTxHash }) break } catch (err) { diff --git a/src/routes/safe/components/Apps/components/ConfirmTransactionModal.tsx b/src/routes/safe/components/Apps/components/ConfirmTransactionModal.tsx index c9e279db..01353288 100644 --- a/src/routes/safe/components/Apps/components/ConfirmTransactionModal.tsx +++ b/src/routes/safe/components/Apps/components/ConfirmTransactionModal.tsx @@ -69,9 +69,8 @@ type OwnProps = { safeAddress: string safeName: string ethBalance: string - onCancel: () => void onUserConfirm: (safeTxHash: string) => void - onUserTxReject: () => void + onTxReject: () => void onClose: () => void } @@ -82,16 +81,20 @@ const ConfirmTransactionModal = ({ safeAddress, ethBalance, safeName, - onCancel, onUserConfirm, onClose, - onUserTxReject, + onTxReject, }: OwnProps): React.ReactElement | null => { const dispatch = useDispatch() if (!isOpen) { return null } + const handleTxRejection = () => { + onTxReject() + onClose() + } + const handleUserConfirmation = (safeTxHash: string): void => { onUserConfirm(safeTxHash) onClose() @@ -113,10 +116,9 @@ const ConfirmTransactionModal = ({ navigateToTransactionsTab: false, }, handleUserConfirmation, - onUserTxReject, + handleTxRejection, ), ) - onClose() } const areTxsMalformed = txs.some((t) => !isTxValid(t)) @@ -165,13 +167,13 @@ const ConfirmTransactionModal = ({ footer={ } - onClose={onClose} + onClose={handleTxRejection} /> ) } diff --git a/src/routes/safe/components/Apps/hooks/useIframeMessageHandler.ts b/src/routes/safe/components/Apps/hooks/useIframeMessageHandler.ts index aa165f4d..8f751a39 100644 --- a/src/routes/safe/components/Apps/hooks/useIframeMessageHandler.ts +++ b/src/routes/safe/components/Apps/hooks/useIframeMessageHandler.ts @@ -12,6 +12,7 @@ import { } from '@gnosis.pm/safe-apps-sdk' import { useDispatch, useSelector } from 'react-redux' import { useEffect, useCallback, MutableRefObject } from 'react' +import { getTxServiceHost } from 'src/config/' import { safeEthBalanceSelector, safeNameSelector, @@ -85,7 +86,7 @@ const useIframeMessageHandler = ( } case SDK_MESSAGES.SAFE_APP_SDK_INITIALIZED: { - const message = { + const safeInfoMessage = { messageId: INTERFACE_MESSAGES.ON_SAFE_INFO, data: { safeAddress: safeAddress as string, @@ -93,8 +94,15 @@ const useIframeMessageHandler = ( ethBalance: ethBalance as string, }, } + const envInfoMessage = { + messageId: INTERFACE_MESSAGES.ENV_INFO, + data: { + txServiceUrl: getTxServiceHost(), + }, + } - sendMessageToIframe(message) + sendMessageToIframe(safeInfoMessage) + sendMessageToIframe(envInfoMessage) break } default: { diff --git a/src/routes/safe/components/Apps/index.tsx b/src/routes/safe/components/Apps/index.tsx index 3d7acdd1..d34d8dd4 100644 --- a/src/routes/safe/components/Apps/index.tsx +++ b/src/routes/safe/components/Apps/index.tsx @@ -102,7 +102,7 @@ const Apps = (): React.ReactElement => { ) } - const onUserTxReject = () => { + const onTxReject = () => { sendMessageToIframe( { messageId: INTERFACE_MESSAGES.TRANSACTION_REJECTED, data: {} }, confirmTransactionModal.requestId, @@ -212,10 +212,9 @@ const Apps = (): React.ReactElement => { ethBalance={ethBalance as string} safeName={safeName as string} txs={confirmTransactionModal.txs} - onCancel={closeConfirmationModal} onClose={closeConfirmationModal} onUserConfirm={onUserTxConfirm} - onUserTxReject={onUserTxReject} + onTxReject={onTxReject} /> ) From e9468790b56b5ef3e106cc4072db09e94dd91b8e Mon Sep 17 00:00:00 2001 From: Fernando Date: Tue, 22 Sep 2020 16:06:06 -0300 Subject: [PATCH 06/11] (Fix) Recipient address used as name as well (#1387) * fix address being used as name * Restore ENS name when sending transaction * use `addressName` as default value if it happens that the name in the addressBook is not defined * use resolvedAddress to filter by address in the address book Co-authored-by: Daniel Sanchez Co-authored-by: Mati Dastugue --- .../SendModal/screens/AddressBookInput/index.tsx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx index f766ea53..db9b9c90 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/AddressBookInput/index.tsx @@ -103,13 +103,23 @@ const AddressBookInput = ({ const { address, name } = adbkEntry return ( name.toLowerCase().includes(normalizedAddress.toLowerCase()) || - address.toLowerCase().includes(normalizedAddress.toLowerCase()) + address.toLowerCase().includes(resolvedAddress.toLowerCase()) ) }) setADBKList(filteredADBK) if (!addressErrorMessage) { + // base case if isENSDomain we set the domain as the name + // if address does not exist in address book we use blank name + let addressName = isENSDomain ? normalizedAddress : '' + + // if address is valid, and is in the address book, then we use the stored values + if (filteredADBK.length === 1) { + const addressBookContact = filteredADBK[0] + addressName = addressBookContact.name ?? addressName + } + setSelectedEntry({ - name: normalizedAddress, + name: addressName, address: resolvedAddress, }) } From eec6e64c84717c1e676fdfeb5bcdcf424f9fcfb8 Mon Sep 17 00:00:00 2001 From: Fernando Date: Tue, 22 Sep 2020 16:19:43 -0300 Subject: [PATCH 07/11] (Chore) Type fixes and refactor (#1376) * use `safeFeaturesEnabled` selector also organized a bit the code (styles) and added Types for the `ChooseTxType` component * fix `getGnosisSafeInstanceAt` return type * add types to `safeStorage` refactor `getSafeName` * use redux selector to obtain master contract version * fix return type Co-authored-by: Daniel Sanchez --- src/logic/contracts/safeContracts.ts | 6 +- src/logic/safe/store/actions/fetchSafe.ts | 3 +- src/logic/safe/utils/safeStorage.ts | 15 ++--- src/logic/safe/utils/safeVersion.ts | 2 +- src/routes/load/container/Load.tsx | 2 +- .../SendModal/screens/ChooseTxType/index.tsx | 62 +++++-------------- .../SendModal/screens/ChooseTxType/style.ts | 45 ++++++++++++++ 7 files changed, 69 insertions(+), 66 deletions(-) create mode 100644 src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style.ts diff --git a/src/logic/contracts/safeContracts.ts b/src/logic/contracts/safeContracts.ts index d2992add..68d971f9 100644 --- a/src/logic/contracts/safeContracts.ts +++ b/src/logic/contracts/safeContracts.ts @@ -98,11 +98,9 @@ export const estimateGasForDeployingSafe = async ( return gas * parseInt(gasPrice, 10) } -export const getGnosisSafeInstanceAt = async (safeAddress: string): Promise => { +export const getGnosisSafeInstanceAt = (safeAddress: string): GnosisSafe => { const web3 = getWeb3() - const gnosisSafe = await new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown as GnosisSafe - - return gnosisSafe + return new web3.eth.Contract(GnosisSafeSol.abi as AbiItem[], safeAddress) as unknown as GnosisSafe } const cleanByteCodeMetadata = (bytecode: string): string => { diff --git a/src/logic/safe/store/actions/fetchSafe.ts b/src/logic/safe/store/actions/fetchSafe.ts index 6821a997..80ca31a5 100644 --- a/src/logic/safe/store/actions/fetchSafe.ts +++ b/src/logic/safe/store/actions/fetchSafe.ts @@ -17,6 +17,7 @@ import { ModulePair, SafeOwner, SafeRecordProps } from 'src/logic/safe/store/mod import { Action, Dispatch } from 'redux' import { SENTINEL_ADDRESS } from 'src/logic/contracts/safeContracts' import { AppReduxState } from 'src/store' +import { latestMasterContractVersionSelector } from '../selectors' const buildOwnersFrom = (safeOwners: string[], localSafe?: SafeRecordProps): List => { const ownersList = safeOwners.map((ownerAddress) => { @@ -157,7 +158,7 @@ export default (safeAdd: string) => async ( try { const safeAddress = checksumAddress(safeAdd) const safeName = (await getSafeName(safeAddress)) || 'LOADED SAFE' - const latestMasterContractVersion = getState().safes.get('latestMasterContractVersion') + const latestMasterContractVersion = latestMasterContractVersionSelector(getState()) const safeProps = await buildSafe(safeAddress, safeName, latestMasterContractVersion) dispatch(addSafe(safeProps)) diff --git a/src/logic/safe/utils/safeStorage.ts b/src/logic/safe/utils/safeStorage.ts index 3ef09f7c..d2fccea0 100644 --- a/src/logic/safe/utils/safeStorage.ts +++ b/src/logic/safe/utils/safeStorage.ts @@ -6,23 +6,16 @@ export const DEFAULT_SAFE_KEY = 'DEFAULT_SAFE' type StoredSafes = Record -export const loadStoredSafes = async (): Promise => { - const safes = await loadFromStorage(SAFES_KEY) - - return safes +export const loadStoredSafes = (): Promise => { + return loadFromStorage(SAFES_KEY) } export const getSafeName = async (safeAddress: string): Promise => { const safes = await loadStoredSafes() - if (!safes) { - return undefined - } - const safe = safes[safeAddress] - - return safe ? safe.name : undefined + return safes?.[safeAddress]?.name } -export const saveSafes = async (safes) => { +export const saveSafes = async (safes: StoredSafes): Promise => { try { await saveToStorage(SAFES_KEY, safes) } catch (err) { diff --git a/src/logic/safe/utils/safeVersion.ts b/src/logic/safe/utils/safeVersion.ts index 38c47e69..5fe5ee34 100644 --- a/src/logic/safe/utils/safeVersion.ts +++ b/src/logic/safe/utils/safeVersion.ts @@ -71,7 +71,7 @@ export const getCurrentMasterContractLastVersion = async (): Promise => export const getSafeVersionInfo = async (safeAddress: string): Promise => { try { - const safeMaster = await getGnosisSafeInstanceAt(safeAddress) + const safeMaster = getGnosisSafeInstanceAt(safeAddress) const lastSafeVersion = await getCurrentMasterContractLastVersion() return checkIfSafeNeedsUpdate(safeMaster, lastSafeVersion) } catch (err) { diff --git a/src/routes/load/container/Load.tsx b/src/routes/load/container/Load.tsx index 49809c26..d32b51f8 100644 --- a/src/routes/load/container/Load.tsx +++ b/src/routes/load/container/Load.tsx @@ -62,7 +62,7 @@ const Load = (): React.ReactElement => { safeAddress = checksumAddress(safeAddress) const ownerNames = getNamesFrom(values) - const gnosisSafe = await getGnosisSafeInstanceAt(safeAddress) + const gnosisSafe = getGnosisSafeInstanceAt(safeAddress) const ownerAddresses = await gnosisSafe.methods.getOwners().call() const owners = getOwnersFrom(ownerNames, ownerAddresses.slice().sort()) diff --git a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx index 19d4ff90..371dfb7a 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/index.tsx @@ -1,13 +1,9 @@ import IconButton from '@material-ui/core/IconButton' -import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import classNames from 'classnames/bind' import * as React from 'react' import { useSelector } from 'react-redux' -import Collectible from '../assets/collectibles.svg' -import Token from '../assets/token.svg' - import { mustBeEthereumContractAddress } from 'src/components/forms/validator' import Button from 'src/components/layout/Button' import Col from 'src/components/layout/Col' @@ -15,54 +11,24 @@ import Hairline from 'src/components/layout/Hairline' import Img from 'src/components/layout/Img' import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' +import { safeFeaturesEnabledSelector } from 'src/logic/safe/store/selectors' +import { useStyles } from 'src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style' import ContractInteractionIcon from 'src/routes/safe/components/Transactions/TxsTable/TxType/assets/custom.svg' -import { safeSelector } from 'src/logic/safe/store/selectors' -import { lg, md, sm } from 'src/theme/variables' -const useStyles = makeStyles({ - heading: { - padding: `${md} ${lg}`, - justifyContent: 'space-between', - boxSizing: 'border-box', - maxHeight: '75px', - }, - manage: { - fontSize: lg, - }, - disclaimer: { - marginBottom: `-${md}`, - paddingTop: md, - textAlign: 'center', - }, - disclaimerText: { - fontSize: md, - }, - closeIcon: { - height: '35px', - width: '35px', - }, - buttonColumn: { - padding: '52px 0', - '& > button': { - fontSize: md, - fontFamily: 'Averta', - }, - }, - firstButton: { - boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)', - marginBottom: 15, - }, - iconSmall: { - fontSize: 16, - }, - leftIcon: { - marginRight: sm, - }, -}) +import Collectible from '../assets/collectibles.svg' +import Token from '../assets/token.svg' -const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }) => { +type ActiveScreen = 'sendFunds' | 'sendCollectible' | 'contractInteraction' + +interface ChooseTxTypeProps { + onClose: () => void + recipientAddress: string + setActiveScreen: React.Dispatch> +} + +const ChooseTxType = ({ onClose, recipientAddress, setActiveScreen }: ChooseTxTypeProps): React.ReactElement => { const classes = useStyles() - const { featuresEnabled } = useSelector(safeSelector) || {} + const featuresEnabled = useSelector(safeFeaturesEnabledSelector) const erc721Enabled = featuresEnabled?.includes('ERC721') const [disableContractInteraction, setDisableContractInteraction] = React.useState(!!recipientAddress) diff --git a/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style.ts b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style.ts new file mode 100644 index 00000000..490db3cc --- /dev/null +++ b/src/routes/safe/components/Balances/SendModal/screens/ChooseTxType/style.ts @@ -0,0 +1,45 @@ +import { createStyles, makeStyles } from '@material-ui/core/styles' +import { lg, md, sm } from 'src/theme/variables' + +export const useStyles = makeStyles( + createStyles({ + heading: { + padding: `${md} ${lg}`, + justifyContent: 'space-between', + boxSizing: 'border-box', + maxHeight: '75px', + }, + manage: { + fontSize: lg, + }, + disclaimer: { + marginBottom: `-${md}`, + paddingTop: md, + textAlign: 'center', + }, + disclaimerText: { + fontSize: md, + }, + closeIcon: { + height: '35px', + width: '35px', + }, + buttonColumn: { + padding: '52px 0', + '& > button': { + fontSize: md, + fontFamily: 'Averta', + }, + }, + firstButton: { + boxShadow: '1px 2px 10px 0 rgba(212, 212, 211, 0.59)', + marginBottom: 15, + }, + iconSmall: { + fontSize: 16, + }, + leftIcon: { + marginRight: sm, + }, + }), +) From a764a23e660252f5ed71d6c3e22fefde97497d65 Mon Sep 17 00:00:00 2001 From: Fernando Date: Wed, 23 Sep 2020 09:39:25 -0300 Subject: [PATCH 08/11] (Fix) (development) Missing collectibles and Safe version (#1375) * use `updateSafe` instead of `addSafe` * fix SAFE_UPDATE reducer - treat every key individually * allow to load owners on the first request * Set UPDATE_SAFE to individually handling all props * Handle List special case * Add comment to list check Co-authored-by: Daniel Sanchez --- src/logic/safe/store/actions/fetchSafe.ts | 51 +++++++++++------------ src/logic/safe/store/reducer/safe.ts | 28 ++++++++++++- 2 files changed, 51 insertions(+), 28 deletions(-) diff --git a/src/logic/safe/store/actions/fetchSafe.ts b/src/logic/safe/store/actions/fetchSafe.ts index 80ca31a5..0b54ed9c 100644 --- a/src/logic/safe/store/actions/fetchSafe.ts +++ b/src/logic/safe/store/actions/fetchSafe.ts @@ -6,7 +6,6 @@ import { getLocalSafe, getSafeName } from 'src/logic/safe/utils' import { enabledFeatures, safeNeedsUpdate } from 'src/logic/safe/utils/safeVersion' import { sameAddress } from 'src/logic/wallets/ethAddresses' import { getBalanceInEtherOf } from 'src/logic/wallets/getWeb3' -import addSafe from 'src/logic/safe/store/actions/addSafe' import addSafeOwner from 'src/logic/safe/store/actions/addSafeOwner' import removeSafeOwner from 'src/logic/safe/store/actions/removeSafeOwner' import updateSafe from 'src/logic/safe/store/actions/updateSafe' @@ -115,7 +114,7 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: Dispatch ]) // Converts from [ { address, ownerName} ] to address array - const localOwners = localSafe ? localSafe.owners.map((localOwner) => localOwner.address) : undefined + const localOwners = localSafe ? localSafe.owners.map((localOwner) => localOwner.address) : [] dispatch( updateSafe({ @@ -127,30 +126,27 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: Dispatch ) // If the remote owners does not contain a local address, we remove that local owner - if (localOwners) { - localOwners.forEach((localAddress) => { - const remoteOwnerIndex = remoteOwners.findIndex((remoteAddress) => sameAddress(remoteAddress, localAddress)) - if (remoteOwnerIndex === -1) { - dispatch(removeSafeOwner({ safeAddress, ownerAddress: localAddress })) - } - }) + localOwners.forEach((localAddress) => { + const remoteOwnerIndex = remoteOwners.findIndex((remoteAddress) => sameAddress(remoteAddress, localAddress)) + if (remoteOwnerIndex === -1) { + dispatch(removeSafeOwner({ safeAddress, ownerAddress: localAddress })) + } + }) - // If the remote has an owner that we don't have locally, we add it - remoteOwners.forEach((remoteAddress) => { - const localOwnerIndex = localOwners.findIndex((localAddress) => sameAddress(remoteAddress, localAddress)) - if (localOwnerIndex === -1) { - dispatch( - addSafeOwner({ - safeAddress, - ownerAddress: remoteAddress, - ownerName: 'UNKNOWN', - }), - ) - } - }) - } + // If the remote has an owner that we don't have locally, we add it + remoteOwners.forEach((remoteAddress) => { + const localOwnerIndex = localOwners.findIndex((localAddress) => sameAddress(remoteAddress, localAddress)) + if (localOwnerIndex === -1) { + dispatch( + addSafeOwner({ + safeAddress, + ownerAddress: remoteAddress, + ownerName: 'UNKNOWN', + }), + ) + } + }) } - export default (safeAdd: string) => async ( dispatch: Dispatch, getState: () => AppReduxState, @@ -161,9 +157,12 @@ export default (safeAdd: string) => async ( const latestMasterContractVersion = latestMasterContractVersionSelector(getState()) const safeProps = await buildSafe(safeAddress, safeName, latestMasterContractVersion) - dispatch(addSafe(safeProps)) + // `updateSafe`, as `loadSafesFromStorage` will populate the store previous to this call + // and `addSafe` will only add a newly non-existent safe + // For the case where the safe does not exist in the localStorage, + // `updateSafe` uses a default `notSetValue` to add the Safe to the store + dispatch(updateSafe(safeProps)) } catch (err) { - // eslint-disable-next-line console.error('Error while updating Safe information: ', err) return Promise.resolve() diff --git a/src/logic/safe/store/reducer/safe.ts b/src/logic/safe/store/reducer/safe.ts index becddb83..094cdf80 100644 --- a/src/logic/safe/store/reducer/safe.ts +++ b/src/logic/safe/store/reducer/safe.ts @@ -1,4 +1,4 @@ -import { Map, Set } from 'immutable' +import { Map, Set, List } from 'immutable' import { handleActions } from 'redux-actions' import { ACTIVATE_TOKEN_FOR_ALL_SAFES } from 'src/logic/safe/store/actions/activateTokenForAllSafes' @@ -51,7 +51,31 @@ export default handleActions( return state.updateIn( ['safes', safeAddress], makeSafe({ name: 'LOADED SAFE', address: safeAddress }), - (prevSafe) => prevSafe.merge(safe), + (prevSafe) => { + return prevSafe.withMutations((record) => { + // Every property is updated individually to overcome the issue with nested data being overwritten + const safeProperties = Object.keys(safe) + + // We check each safe property sent in action.payload + safeProperties.forEach((key) => { + if (safe[key] && typeof safe[key] === 'object') { + if (safe[key].length) { + // If type is array we update the array + record.update(key, () => safe[key]) + } else if (safe[key].size) { + // If type is Immutable List we replace current List + // If type is Object we do a merge + List.isList(safe[key]) + ? record.update(key, (current) => current.set(safe[key])) + : record.update(key, (current) => current.merge(safe[key])) + } + } else { + // By default we overwrite the value. This is for strings, numbers and unset values + record.set(key, safe[key]) + } + }) + }) + }, ) }, [ACTIVATE_TOKEN_FOR_ALL_SAFES]: (state: SafeReducerMap, action) => { From d1348713ad086a7c05343d401500a0f491e787fe Mon Sep 17 00:00:00 2001 From: Daniel Sanchez Date: Wed, 23 Sep 2020 17:26:55 +0200 Subject: [PATCH 09/11] Add Yearn Finance app (#1390) * Add Yearn Finance app --- src/routes/safe/components/Apps/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/safe/components/Apps/utils.ts b/src/routes/safe/components/Apps/utils.ts index 00559f92..e5dae153 100644 --- a/src/routes/safe/components/Apps/utils.ts +++ b/src/routes/safe/components/Apps/utils.ts @@ -38,6 +38,8 @@ export const staticAppsList: Array<{ url: string; disabled: boolean }> = [ { url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmQovvfYYMUXjZfNbysQDUEXR8nr55iJRwcYgJQGJR7KEA`, disabled: false }, // TX-Builder { url: `${gnosisAppsUrl}/tx-builder`, disabled: false }, + // Yearn Vaults + { url: `${process.env.REACT_APP_IPFS_GATEWAY}/Qme9HuPPhgCtgfj1CktvaDKhTesMueGCV2Kui1Sqna3Xs9`, disabled: false }, ] export const getAppInfoFromOrigin = (origin: string): Record | null => { From 6f707a632b07e783fc08e6f2f0c7fab3c4ecafd1 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 23 Sep 2020 15:14:49 -0300 Subject: [PATCH 10/11] (Feature) #588 Use addressBook names when creating/loading safe (#1377) * Fix addressbook types Restructure addressbook store type * Add more safe types * Fix imports * Removes .toJS() usage * Fix condition for saving addressBook * Types & remove send button from addressbook if user not an owner * Add types for addressBook actions Remove unused saveAndUpdateAddressBook action * Refactor addressBook: make it global and removes immutableJS Removes unused addAddressBook action * Fix edit and remove entries style when user is not owner * Adds and updates safe name in addressBook * Adds checkIfOwnerWasDeletedFromAddressBook Let the user remove owners users without adding them again each time the safe loads * Simplify loadAddressBookFromStorage * Fix compilation errors included in pr #1301 * Uses sameAddress function * Add migration function for old stored address books * Replaces shouldAvoidUpdatesNotifications with addAddressBookEntryOptions on addAddressBookEntry * Unify return on getOwnersWithNameFromAddressBook * Adds the addressbook names in safe load * Reword shouldAvoidUpdatesNotifications * Replaces adbk with addressBook * Renames adbk to AddressBook * Types on Open and Layout * Remove unused actions and selectors * Replaces initialValuesFrom to a hook and retrieves the ownerName * Uses addressBook names in safe creation * Fix owner name on creating safe * Renames getNameFromAddressBook to getNameFromAddressBookSelector * Fixs addOrUpdateAddressBookEntry action * Updates addressbook on safe load * Revert load update addressbook behaviour * Renames checkIfOwnerWasDeletedFromAddressBook to checkIfEntryWasDeletedFromAddressBook * Feedback * Type review informaiton * Adds ADD_OR_UPDATE_SAFE action * Replaces addSafe with addOrUpdateSafe on addSafeHandler * Exports isValidAddressBookName util function * Adds isValidAddressBookName test * Add tests for checkIfEntryWasDeletedFromAddressBook * Fix saveAddressBook test * Fix fetchSafeTokens.test.ts * Add update individually safe props in addOrUpdate * Fix updating addressbook entries on safe load/create * Fix always loading safe as LOADED SAFE instead of safe name * Fix adding owner as UNKNOWN on addressBook when adding new owner Co-authored-by: Daniel Sanchez --- .../addressBook/store/reducer/addressBook.ts | 10 +- .../addressBook/store/selectors/index.ts | 2 +- .../utils/__tests__/addressBookUtils.test.ts | 121 +++++++++++++++- src/logic/addressBook/utils/index.ts | 68 +++++++-- .../__tests__/fetchSafeTokens.test.ts | 4 +- src/logic/safe/hooks/useLoadSafe.tsx | 2 +- .../safe/store/actions/addOrUpdateSafe.ts | 9 ++ src/logic/safe/store/actions/fetchSafe.ts | 1 + .../safe/store/middleware/safeStorage.ts | 59 ++++---- src/logic/safe/store/reducer/safe.ts | 68 +++++---- .../load/components/OwnerList/index.tsx | 132 +++++++----------- .../load/components/OwnerList/styles.ts | 52 +++++++ src/routes/load/container/Load.tsx | 6 +- src/routes/open/components/Layout.tsx | 42 +++++- .../components/ReviewInformation/index.tsx | 21 ++- .../SafeOwnersConfirmationsForm/index.tsx | 30 ++-- .../SafeOwnersConfirmationsForm/style.ts | 3 +- src/routes/open/container/Open.tsx | 56 +++----- src/routes/open/container/actions.ts | 5 - src/routes/open/container/selector.ts | 9 -- src/routes/opening/index.tsx | 9 +- .../ManageOwners/AddOwnerModal/index.tsx | 3 +- .../ManageOwners/ReplaceOwnerModal/index.tsx | 6 +- .../IncomingTxDescription/index.tsx | 4 +- .../OwnersColumn/OwnerComponent.tsx | 4 +- .../TxDescription/CustomDescription.tsx | 4 +- .../TxDescription/SettingsDescription.tsx | 6 +- .../TxDescription/TransferDescription.tsx | 4 +- 28 files changed, 487 insertions(+), 253 deletions(-) create mode 100644 src/logic/safe/store/actions/addOrUpdateSafe.ts create mode 100644 src/routes/load/components/OwnerList/styles.ts delete mode 100644 src/routes/open/container/actions.ts delete mode 100644 src/routes/open/container/selector.ts diff --git a/src/logic/addressBook/store/reducer/addressBook.ts b/src/logic/addressBook/store/reducer/addressBook.ts index 55f3a493..ad23f4fd 100644 --- a/src/logic/addressBook/store/reducer/addressBook.ts +++ b/src/logic/addressBook/store/reducer/addressBook.ts @@ -11,8 +11,8 @@ import { getValidAddressBookName } from 'src/logic/addressBook/utils' export const ADDRESS_BOOK_REDUCER_ID = 'addressBook' -export const buildAddressBook = (storedAdbk: AddressBookState): AddressBookState => { - return storedAdbk.map((addressBookEntry) => { +export const buildAddressBook = (storedAddressBook: AddressBookState): AddressBookState => { + return storedAddressBook.map((addressBookEntry) => { const { address, name } = addressBookEntry return makeAddressBookEntry({ address: checksumAddress(address), name }) }) @@ -51,14 +51,16 @@ export default handleActions( return state }, [ADD_OR_UPDATE_ENTRY]: (state, action) => { - const { entry, entryAddress } = action.payload + const { entry } = action.payload // Only updates entries with valid names const validName = getValidAddressBookName(entry.name) if (!validName) { return state } - const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entryAddress) + + const entryIndex = state.findIndex((oldEntry) => oldEntry.address === entry.address) + if (entryIndex >= 0) { state[entryIndex] = entry } else { diff --git a/src/logic/addressBook/store/selectors/index.ts b/src/logic/addressBook/store/selectors/index.ts index ffa4fbbe..7460909f 100644 --- a/src/logic/addressBook/store/selectors/index.ts +++ b/src/logic/addressBook/store/selectors/index.ts @@ -8,7 +8,7 @@ import { AddressBookState } from 'src/logic/addressBook/model/addressBook' export const addressBookSelector = (state: AppReduxState): AddressBookState => state[ADDRESS_BOOK_REDUCER_ID] -export const getNameFromAddressBook = createSelector( +export const getNameFromAddressBookSelector = createSelector( addressBookSelector, (_, address) => address, (addressBook, address) => { diff --git a/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts b/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts index 63599d72..4541c2f7 100644 --- a/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts +++ b/src/logic/addressBook/utils/__tests__/addressBookUtils.test.ts @@ -1,9 +1,11 @@ import { List } from 'immutable' import { + checkIfEntryWasDeletedFromAddressBook, getAddressBookFromStorage, getAddressesListFromAddressBook, getNameFromAddressBook, getOwnersWithNameFromAddressBook, + isValidAddressBookName, migrateOldAddressBook, OldAddressBookEntry, OldAddressBookType, @@ -85,6 +87,7 @@ describe('getOwnersWithNameFromAddressBook', () => { }) }) +jest.mock('src/utils/storage/index') describe('saveAddressBook', () => { const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' @@ -92,19 +95,27 @@ describe('saveAddressBook', () => { const entry1 = getMockAddressBookEntry(mockAdd1, 'test1') const entry2 = getMockAddressBookEntry(mockAdd2, 'test2') const entry3 = getMockAddressBookEntry(mockAdd3, 'test3') + afterAll(() => { + jest.unmock('src/utils/storage/index') + }) it('It should save a given addressBook to the localStorage', async () => { // given const addressBook: AddressBookState = [entry1, entry2, entry3] // when - // @ts-ignore await saveAddressBook(addressBook) - const storedAdBk = await getAddressBookFromStorage() + + const storageUtils = require('src/utils/storage/index') + const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => JSON.stringify(addressBook)) + + const storedAddressBook = await getAddressBookFromStorage() + // @ts-ignore - let result = buildAddressBook(storedAdBk) + let result = buildAddressBook(storedAddressBook) // then expect(result).toStrictEqual(addressBook) + expect(spy).toHaveBeenCalled() }) }) @@ -136,18 +147,19 @@ describe('migrateOldAddressBook', () => { }) }) -jest.mock('src/utils/storage/index') describe('getAddressBookFromStorage', () => { const safeAddress1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' const safeAddress2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' const mockAdd1 = '0x9163c2F4452E3399CB60AAf737231Af87548DA91' const mockAdd2 = '0xC4e446Da9C3D37385C86488294C6758c4e25dbD8' + beforeAll(() => { + jest.mock('src/utils/storage/index') + }) afterAll(() => { jest.unmock('src/utils/storage/index') }) it('It should return null if no addressBook in storage', async () => { // given - const expectedResult = null const storageUtils = require('src/utils/storage/index') const spy = storageUtils.loadFromStorage.mockImplementationOnce(() => null) @@ -199,3 +211,102 @@ describe('getAddressBookFromStorage', () => { expect(spy).toHaveBeenCalled() }) }) + +describe('isValidAddressBookName', () => { + it('It should return false if given a blacklisted name like UNKNOWN', () => { + // given + const addressNameInput = 'UNKNOWN' + + const expectedResult = false + + // when + const result = isValidAddressBookName(addressNameInput) + + // then + expect(result).toStrictEqual(expectedResult) + }) + it('It should return false if given a blacklisted name like MY WALLET', () => { + // given + const addressNameInput = 'MY WALLET' + + const expectedResult = false + + // when + const result = isValidAddressBookName(addressNameInput) + + // then + expect(result).toStrictEqual(expectedResult) + }) + it('It should return false if given a blacklisted name like OWNER #', () => { + // given + const addressNameInput = 'OWNER #' + + const expectedResult = false + + // when + const result = isValidAddressBookName(addressNameInput) + + // then + expect(result).toStrictEqual(expectedResult) + }) + it('It should return true if the given address name is valid', () => { + // given + const addressNameInput = 'User' + + const expectedResult = true + + // when + const result = isValidAddressBookName(addressNameInput) + + // then + expect(result).toEqual(expectedResult) + }) +}) + +describe('checkIfEntryWasDeletedFromAddressBook', () => { + const mockAdd1 = '0x696fd93D725d84acfFf6c62a1fe8C94E1c9E934A' + const mockAdd2 = '0x2C7aC78b01Be0FC66AD29b684ffAb0C93B381D00' + const mockAdd3 = '0x537BD452c3505FC07bA242E437bD29D4E1DC9126' + const entry1 = getMockAddressBookEntry(mockAdd1, 'test1') + const entry2 = getMockAddressBookEntry(mockAdd2, 'test2') + const entry3 = getMockAddressBookEntry(mockAdd3, 'test3') + it('It should return true if a given entry was deleted from addressBook', () => { + // given + const addressBookEntry = entry1 + const addressBook: AddressBookState = [entry2, entry3] + const safeAlreadyLoaded = true + const expectedResult = true + + // when + const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded) + + // then + expect(result).toEqual(expectedResult) + }) + it('It should return false if a given entry was not deleted from addressBook', () => { + // given + const addressBookEntry = entry1 + const addressBook: AddressBookState = [entry1, entry2, entry3] + const safeAlreadyLoaded = true + const expectedResult = false + + // when + const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded) + + // then + expect(result).toEqual(expectedResult) + }) + it('It should return false if the safe was not already loaded', () => { + // given + const addressBookEntry = entry1 + const addressBook: AddressBookState = [entry1, entry2, entry3] + const safeAlreadyLoaded = false + const expectedResult = false + + // when + const result = checkIfEntryWasDeletedFromAddressBook(addressBookEntry, addressBook, safeAlreadyLoaded) + + // then + expect(result).toEqual(expectedResult) + }) +}) diff --git a/src/logic/addressBook/utils/index.ts b/src/logic/addressBook/utils/index.ts index 73743000..fed359a9 100644 --- a/src/logic/addressBook/utils/index.ts +++ b/src/logic/addressBook/utils/index.ts @@ -1,6 +1,6 @@ import { List } from 'immutable' import { loadFromStorage, saveToStorage } from 'src/utils/storage' -import { AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { SafeOwner } from 'src/logic/safe/store/models/safe' import { sameAddress } from 'src/logic/wallets/ethAddresses' @@ -16,6 +16,8 @@ export type OldAddressBookType = { [safeAddress: string]: [OldAddressBookEntry] } +const ADDRESSBOOK_INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET'] + export const migrateOldAddressBook = (oldAddressBook: OldAddressBookType): AddressBookState => { const values: AddressBookState = [] const adbkValues = Object.values(oldAddressBook) @@ -56,19 +58,31 @@ export const saveAddressBook = async (addressBook: AddressBookState): Promise addressBook.map((entry) => entry.address) -export const getNameFromAddressBook = (addressBook: AddressBookState, userAddress: string): string | null => { +type GetNameFromAddressBookOptions = { + filterOnlyValidName: boolean +} + +export const getNameFromAddressBook = ( + addressBook: AddressBookState, + userAddress: string, + options?: GetNameFromAddressBookOptions, +): string | null => { const entry = addressBook.find((addressBookItem) => addressBookItem.address === userAddress) if (entry) { - return entry.name + return options?.filterOnlyValidName ? getValidAddressBookName(entry.name) : entry.name } return null } -export const getValidAddressBookName = (addressbookName: string): string | null => { - const INVALID_NAMES = ['UNKNOWN', 'OWNER #', 'MY WALLET'] - const isInvalid = INVALID_NAMES.find((invalidName) => addressbookName.toUpperCase().includes(invalidName)) - if (isInvalid) return null - return addressbookName +export const isValidAddressBookName = (addressBookName: string): boolean => { + const hasInvalidName = ADDRESSBOOK_INVALID_NAMES.find((invalidName) => + addressBookName.toUpperCase().includes(invalidName), + ) + return !hasInvalidName +} + +export const getValidAddressBookName = (addressBookName: string): string | null => { + return isValidAddressBookName(addressBookName) ? addressBookName : null } export const getOwnersWithNameFromAddressBook = ( @@ -86,3 +100,41 @@ export const getOwnersWithNameFromAddressBook = ( } }) } + +export const formatAddressListToAddressBookNames = ( + addressBook: AddressBookState, + addresses: string[], +): AddressBookEntry[] => { + if (!addresses.length) { + return [] + } + return addresses.map((address) => { + const ownerName = getNameFromAddressBook(addressBook, address) + return { + address: address, + name: ownerName || '', + } + }) +} + +/** + * If the safe is not loaded, the owner wasn't not deleted + * If the safe is already loaded and the owner has a valid name, will return true if the address is not already on the addressBook + * @param name + * @param address + * @param addressBook + * @param safeAlreadyLoaded + */ +export const checkIfEntryWasDeletedFromAddressBook = ( + { name, address }: AddressBookEntry, + addressBook: AddressBookState, + safeAlreadyLoaded: boolean, +): boolean => { + if (!safeAlreadyLoaded) { + return false + } + + const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name) + const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address)) + return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook +} diff --git a/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts b/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts index 246a2664..f1298e76 100644 --- a/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts +++ b/src/logic/currencyValues/__tests__/fetchSafeTokens.test.ts @@ -48,6 +48,8 @@ describe('fetchTokenCurrenciesBalances', () => { // then expect(result).toStrictEqual(expectedResult) expect(axios.get).toHaveBeenCalled() - expect(axios.get).toBeCalledWith(`${apiUrl}safes/${safeAddress}/balances/usd/`, { params: { limit: 3000 } }) + expect(axios.get).toBeCalledWith(`${apiUrl}safes/${safeAddress}/balances/usd/?exclude_spam=true`, { + params: { limit: 3000 }, + }) }) }) diff --git a/src/logic/safe/hooks/useLoadSafe.tsx b/src/logic/safe/hooks/useLoadSafe.tsx index 9d6e15df..6a44a7c4 100644 --- a/src/logic/safe/hooks/useLoadSafe.tsx +++ b/src/logic/safe/hooks/useLoadSafe.tsx @@ -22,13 +22,13 @@ export const useLoadSafe = (safeAddress?: string): void => { return dispatch(fetchSafeTokens(safeAddress)) }) .then(() => { - dispatch(loadAddressBookFromStorage()) dispatch(fetchSafeCreationTx(safeAddress)) dispatch(fetchTransactions(safeAddress)) return dispatch(addViewedSafe(safeAddress)) }) } } + dispatch(loadAddressBookFromStorage()) fetchData() }, [dispatch, safeAddress]) diff --git a/src/logic/safe/store/actions/addOrUpdateSafe.ts b/src/logic/safe/store/actions/addOrUpdateSafe.ts new file mode 100644 index 00000000..8aafa0ea --- /dev/null +++ b/src/logic/safe/store/actions/addOrUpdateSafe.ts @@ -0,0 +1,9 @@ +import { createAction } from 'redux-actions' + +import { SafeRecordProps } from '../models/safe' + +export const ADD_OR_UPDATE_SAFE = 'ADD_OR_UPDATE_SAFE' + +export const addOrUpdateSafe = createAction(ADD_OR_UPDATE_SAFE, (safe: SafeRecordProps) => ({ + safe, +})) diff --git a/src/logic/safe/store/actions/fetchSafe.ts b/src/logic/safe/store/actions/fetchSafe.ts index 0b54ed9c..a398ffb7 100644 --- a/src/logic/safe/store/actions/fetchSafe.ts +++ b/src/logic/safe/store/actions/fetchSafe.ts @@ -119,6 +119,7 @@ export const checkAndUpdateSafe = (safeAdd: string) => async (dispatch: Dispatch dispatch( updateSafe({ address: safeAddress, + name: localSafe?.name, modules: buildModulesLinkedList(modules?.array, modules?.next), nonce: Number(remoteNonce), threshold: Number(remoteThreshold), diff --git a/src/logic/safe/store/middleware/safeStorage.ts b/src/logic/safe/store/middleware/safeStorage.ts index 4f588d0b..f37ef047 100644 --- a/src/logic/safe/store/middleware/safeStorage.ts +++ b/src/logic/safe/store/middleware/safeStorage.ts @@ -13,16 +13,19 @@ import { SET_DEFAULT_SAFE } from 'src/logic/safe/store/actions/setDefaultSafe' import { UPDATE_SAFE } from 'src/logic/safe/store/actions/updateSafe' import { getActiveTokensAddressesForAllSafes, safesMapSelector } from 'src/logic/safe/store/selectors' import { checksumAddress } from 'src/utils/checksumAddress' -import { AddressBookEntry, AddressBookState, makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' import { addOrUpdateAddressBookEntry } from 'src/logic/addressBook/store/actions/addOrUpdateAddressBookEntry' -import { getValidAddressBookName } from 'src/logic/addressBook/utils' +import { checkIfEntryWasDeletedFromAddressBook, isValidAddressBookName } from 'src/logic/addressBook/utils' import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { sameAddress } from 'src/logic/wallets/ethAddresses' +import { updateAddressBookEntry } from 'src/logic/addressBook/store/actions/updateAddressBookEntry' +import { ADD_OR_UPDATE_SAFE } from 'src/logic/safe/store/actions/addOrUpdateSafe' const watchedActions = [ ADD_SAFE, UPDATE_SAFE, REMOVE_SAFE, + ADD_OR_UPDATE_SAFE, ADD_SAFE_OWNER, REMOVE_SAFE_OWNER, REPLACE_SAFE_OWNER, @@ -46,30 +49,6 @@ const recalculateActiveTokens = (state) => { saveActiveTokens(activeTokens) } -/** - * If the owner has a valid name that means that should be on the addressBook - * if the owner is not currently on the addressBook, that means the user deleted it - * or that it's a new safe with valid names, so we also check if it's a new safe or an already loaded one - * @param name - * @param address - * @param addressBook - * @param safeAlreadyLoaded -> true if the safe was loaded from the localStorage - */ -// TODO TEST -const checkIfOwnerWasDeletedFromAddressBook = ( - { name, address }: AddressBookEntry, - addressBook: AddressBookState, - safeAlreadyLoaded: boolean, -) => { - if (!safeAlreadyLoaded) { - return false - } - - const addressShouldBeOnTheAddressBook = !!getValidAddressBookName(name) - const isAlreadyInAddressBook = !!addressBook.find((entry) => sameAddress(entry.address, address)) - return addressShouldBeOnTheAddressBook && !isAlreadyInAddressBook -} - const safeStorageMware = (store) => (next) => async (action) => { const handledAction = next(action) @@ -87,22 +66,30 @@ const safeStorageMware = (store) => (next) => async (action) => { } case ADD_SAFE: { const { safe, loadedFromStorage } = action.payload + const safeAlreadyLoaded = + loadedFromStorage || safes.find((safeIterator) => sameAddress(safeIterator.address, safe.address)) + safe.owners.forEach((owner) => { const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name }) - const ownerWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook( + + const ownerWasAlreadyInAddressBook = checkIfEntryWasDeletedFromAddressBook( checksumEntry, addressBook, - loadedFromStorage, + safeAlreadyLoaded, ) if (!ownerWasAlreadyInAddressBook) { dispatch(addAddressBookEntry(checksumEntry, { notifyEntryUpdate: false })) } + const addressAlreadyExists = addressBook.find((entry) => sameAddress(entry.address, checksumEntry.address)) + if (isValidAddressBookName(checksumEntry.name) && addressAlreadyExists) { + dispatch(updateAddressBookEntry(checksumEntry)) + } }) - const safeWasAlreadyInAddressBook = checkIfOwnerWasDeletedFromAddressBook( + const safeWasAlreadyInAddressBook = checkIfEntryWasDeletedFromAddressBook( { address: safe.address, name: safe.name }, addressBook, - loadedFromStorage, + safeAlreadyLoaded, ) if (!safeWasAlreadyInAddressBook) { @@ -114,13 +101,23 @@ const safeStorageMware = (store) => (next) => async (action) => { } break } + case ADD_OR_UPDATE_SAFE: { + const { safe } = action.payload + safe.owners.forEach((owner) => { + const checksumEntry = makeAddressBookEntry({ address: checksumAddress(owner.address), name: owner.name }) + if (isValidAddressBookName(checksumEntry.name)) { + dispatch(addOrUpdateAddressBookEntry(checksumEntry)) + } + }) + break + } case UPDATE_SAFE: { const { activeTokens, name, address } = action.payload if (activeTokens) { recalculateActiveTokens(state) } if (name) { - dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address })), { notifyEntryUpdate: false }) + dispatch(addOrUpdateAddressBookEntry(makeAddressBookEntry({ name, address }))) } break } diff --git a/src/logic/safe/store/reducer/safe.ts b/src/logic/safe/store/reducer/safe.ts index 094cdf80..f6d094bf 100644 --- a/src/logic/safe/store/reducer/safe.ts +++ b/src/logic/safe/store/reducer/safe.ts @@ -15,6 +15,7 @@ import { makeOwner } from 'src/logic/safe/store/models/owner' import makeSafe, { SafeRecordProps } from 'src/logic/safe/store/models/safe' import { checksumAddress } from 'src/utils/checksumAddress' import { SafeReducerMap } from 'src/routes/safe/store/reducer/types/safe' +import { ADD_OR_UPDATE_SAFE } from 'src/logic/safe/store/actions/addOrUpdateSafe' export const SAFE_REDUCER_ID = 'safes' export const DEFAULT_SAFE_INITIAL_STATE = 'NOT_ASKED' @@ -42,6 +43,32 @@ export const buildSafe = (storedSafe: SafeRecordProps): SafeRecordProps => { } } +const updateSafeProps = (prevSafe, safe) => { + return prevSafe.withMutations((record) => { + // Every property is updated individually to overcome the issue with nested data being overwritten + const safeProperties = Object.keys(safe) + + // We check each safe property sent in action.payload + safeProperties.forEach((key) => { + if (safe[key] && typeof safe[key] === 'object') { + if (safe[key].length) { + // If type is array we update the array + record.update(key, () => safe[key]) + } else if (safe[key].size) { + // If type is Immutable List we replace current List + // If type is Object we do a merge + List.isList(safe[key]) + ? record.update(key, (current) => current.set(safe[key])) + : record.update(key, (current) => current.merge(safe[key])) + } + } else { + // By default we overwrite the value. This is for strings, numbers and unset values + record.set(key, safe[key]) + } + }) + }) +} + export default handleActions( { [UPDATE_SAFE]: (state: SafeReducerMap, action) => { @@ -50,32 +77,8 @@ export default handleActions( return state.updateIn( ['safes', safeAddress], - makeSafe({ name: 'LOADED SAFE', address: safeAddress }), - (prevSafe) => { - return prevSafe.withMutations((record) => { - // Every property is updated individually to overcome the issue with nested data being overwritten - const safeProperties = Object.keys(safe) - - // We check each safe property sent in action.payload - safeProperties.forEach((key) => { - if (safe[key] && typeof safe[key] === 'object') { - if (safe[key].length) { - // If type is array we update the array - record.update(key, () => safe[key]) - } else if (safe[key].size) { - // If type is Immutable List we replace current List - // If type is Object we do a merge - List.isList(safe[key]) - ? record.update(key, (current) => current.set(safe[key])) - : record.update(key, (current) => current.merge(safe[key])) - } - } else { - // By default we overwrite the value. This is for strings, numbers and unset values - record.set(key, safe[key]) - } - }) - }) - }, + makeSafe({ name: safe?.name || 'LOADED SAFE', address: safeAddress }), + (prevSafe) => updateSafeProps(prevSafe, safe), ) }, [ACTIVATE_TOKEN_FOR_ALL_SAFES]: (state: SafeReducerMap, action) => { @@ -106,6 +109,19 @@ export default handleActions( return state.setIn(['safes', safe.address], makeSafe(safe)) }, + [ADD_OR_UPDATE_SAFE]: (state: SafeReducerMap, action) => { + const { safe } = action.payload + + if (!state.hasIn(['safes', safe.address])) { + return state.setIn(['safes', safe.address], makeSafe(safe)) + } + + return state.updateIn( + ['safes', safe.address], + makeSafe({ name: 'LOADED SAFE', address: safe.address }), + (prevSafe) => updateSafeProps(prevSafe, safe), + ) + }, [REMOVE_SAFE]: (state: SafeReducerMap, action) => { const safeAddress = action.payload diff --git a/src/routes/load/components/OwnerList/index.tsx b/src/routes/load/components/OwnerList/index.tsx index 80fdfb9c..a172328f 100644 --- a/src/routes/load/components/OwnerList/index.tsx +++ b/src/routes/load/components/OwnerList/index.tsx @@ -1,5 +1,5 @@ import TableContainer from '@material-ui/core/TableContainer' -import { withStyles } from '@material-ui/core/styles' +import { makeStyles } from '@material-ui/core/styles' import React, { useEffect, useState } from 'react' import CopyBtn from 'src/components/CopyBtn' @@ -17,57 +17,13 @@ import Row from 'src/components/layout/Row' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { FIELD_LOAD_ADDRESS, THRESHOLD } from 'src/routes/load/components/fields' import { getOwnerAddressBy, getOwnerNameBy } from 'src/routes/open/components/fields' -import { border, disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables' -const styles = () => ({ - details: { - padding: lg, - borderRight: `solid 1px ${border}`, - height: '100%', - }, - owners: { - display: 'flex', - justifyContent: 'flex-start', - }, - ownerName: { - marginBottom: '15px', - minWidth: '100%', - [`@media (min-width: ${screenSm}px)`]: { - marginBottom: '0', - minWidth: '0', - }, - }, - ownerAddresses: { - alignItems: 'center', - marginLeft: `${sm}`, - }, - address: { - paddingLeft: '6px', - marginRight: sm, - }, - open: { - paddingLeft: sm, - width: 'auto', - '&:hover': { - cursor: 'pointer', - }, - }, - title: { - padding: `${md} ${lg}`, - }, - owner: { - padding: `0 ${lg}`, - marginBottom: '12px', - }, - header: { - padding: `${sm} ${lg}`, - color: disabled, - fontSize: extraSmallFontSize, - }, - name: { - marginRight: `${sm}`, - }, -}) +import { useSelector } from 'react-redux' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' + +import { formatAddressListToAddressBookNames } from 'src/logic/addressBook/utils' +import { AddressBookEntry } from 'src/logic/addressBook/model/addressBook' +import { styles } from './styles' const calculateSafeValues = (owners, threshold, values) => { const initialValues = { ...values } @@ -78,9 +34,20 @@ const calculateSafeValues = (owners, threshold, values) => { return initialValues } +const useAddressBookForOwnersNames = (ownersList: string[]): AddressBookEntry[] => { + const addressBook = useSelector(addressBookSelector) + + return formatAddressListToAddressBookNames(addressBook, ownersList) +} + +const useStyles = makeStyles(styles) + const OwnerListComponent = (props) => { const [owners, setOwners] = useState([]) - const { classes, updateInitialProps, values } = props + const classes = useStyles() + const { updateInitialProps, values } = props + + const ownersWithNames = useAddressBookForOwnersNames(owners) useEffect(() => { let isCurrent = true @@ -121,47 +88,48 @@ const OwnerListComponent = (props) => { - {owners.map((address, index) => ( - - - - - - - - - {address} - - - - - - - ))} + {ownersWithNames.map(({ address, name }, index) => { + const ownerName = name || `Owner #${index + 1}` + return ( + + + + + + + + + {address} + + + + + + + ) + })} ) } -const OwnerListPage = withStyles(styles as any)(OwnerListComponent) - const OwnerList = ({ updateInitialProps }, network) => function LoadSafeOwnerList(controls, { values }): React.ReactElement { return ( <> - + ) diff --git a/src/routes/load/components/OwnerList/styles.ts b/src/routes/load/components/OwnerList/styles.ts new file mode 100644 index 00000000..c5f1e710 --- /dev/null +++ b/src/routes/load/components/OwnerList/styles.ts @@ -0,0 +1,52 @@ +import { border, disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables' +import { createStyles } from '@material-ui/core' + +export const styles = createStyles({ + details: { + padding: lg, + borderRight: `solid 1px ${border}`, + height: '100%', + }, + owners: { + display: 'flex', + justifyContent: 'flex-start', + }, + ownerName: { + marginBottom: '15px', + minWidth: '100%', + [`@media (min-width: ${screenSm}px)`]: { + marginBottom: '0', + minWidth: '0', + }, + }, + ownerAddresses: { + alignItems: 'center', + marginLeft: `${sm}`, + }, + address: { + paddingLeft: '6px', + marginRight: sm, + }, + open: { + paddingLeft: sm, + width: 'auto', + '&:hover': { + cursor: 'pointer', + }, + }, + title: { + padding: `${md} ${lg}`, + }, + owner: { + padding: `0 ${lg}`, + marginBottom: '12px', + }, + header: { + padding: `${sm} ${lg}`, + color: disabled, + fontSize: extraSmallFontSize, + }, + name: { + marginRight: `${sm}`, + }, +}) diff --git a/src/routes/load/container/Load.tsx b/src/routes/load/container/Load.tsx index d32b51f8..a3452bfd 100644 --- a/src/routes/load/container/Load.tsx +++ b/src/routes/load/container/Load.tsx @@ -14,8 +14,8 @@ import { history } from 'src/store' import { SafeOwner, SafeRecordProps } from 'src/logic/safe/store/models/safe' import { List } from 'immutable' import { checksumAddress } from 'src/utils/checksumAddress' -import { addSafe } from 'src/logic/safe/store/actions/addSafe' import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' +import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe' export const loadSafe = async ( safeName: string, @@ -46,8 +46,8 @@ const Load = (): React.ReactElement => { const network = useSelector(networkSelector) const userAddress = useSelector(userAccountSelector) - const addSafeHandler = (safe: SafeRecordProps) => { - dispatch(addSafe(safe)) + const addSafeHandler = async (safe: SafeRecordProps) => { + await dispatch(addOrUpdateSafe(safe)) } const onLoadSafeSubmit = async (values: LoadFormValues) => { let safeAddress = values[FIELD_LOAD_ADDRESS] diff --git a/src/routes/open/components/Layout.tsx b/src/routes/open/components/Layout.tsx index 7a43d735..0aef861f 100644 --- a/src/routes/open/components/Layout.tsx +++ b/src/routes/open/components/Layout.tsx @@ -19,22 +19,43 @@ import { import Welcome from 'src/routes/welcome/components/Layout' import { history } from 'src/store' import { secondary, sm } from 'src/theme/variables' +import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' +import { useSelector } from 'react-redux' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBook } from 'src/logic/addressBook/utils' const { useEffect } = React const getSteps = () => ['Name', 'Owners and confirmations', 'Review'] -const initialValuesFrom = (userAccount, safeProps) => { +type SafeProps = { + name: string + ownerAddresses: any + ownerNames: string + threshold: string +} + +type InitialValuesForm = { + owner0Address?: string + owner0Name?: string + confirmations: string + safeName?: string +} + +const useInitialValuesFrom = (userAccount: string, safeProps?: SafeProps): InitialValuesForm => { + const addressBook = useSelector(addressBookSelector) + const ownerName = getNameFromAddressBook(addressBook, userAccount, { filterOnlyValidName: true }) + if (!safeProps) { return { - [getOwnerNameBy(0)]: 'My Wallet', + [getOwnerNameBy(0)]: ownerName || 'My Wallet', [getOwnerAddressBy(0)]: userAccount, [FIELD_CONFIRMATIONS]: '1', } } let obj = {} const { name, ownerAddresses, ownerNames, threshold } = safeProps - // eslint-disable-next-line no-restricted-syntax + for (const [index, value] of ownerAddresses.entries()) { const safeName = ownerNames[index] ? ownerNames[index] : 'My Wallet' obj = { @@ -66,8 +87,17 @@ const formMutators = { }, } -const Layout = (props) => { - const { network, onCallSafeContractSubmit, provider, safeProps, userAccount } = props +type LayoutProps = { + onCallSafeContractSubmit: (formValues: unknown) => void + safeProps?: SafeProps +} + +const Layout = (props: LayoutProps): React.ReactElement => { + const { onCallSafeContractSubmit, safeProps } = props + + const provider = useSelector(providerNameSelector) + const network = useSelector(networkSelector) + const userAccount = useSelector(userAccountSelector) useEffect(() => { if (provider) { @@ -77,7 +107,7 @@ const Layout = (props) => { const steps = getSteps() - const initialValues = initialValuesFrom(userAccount, safeProps) + const initialValues = useInitialValuesFrom(userAccount, safeProps) return ( <> diff --git a/src/routes/open/components/ReviewInformation/index.tsx b/src/routes/open/components/ReviewInformation/index.tsx index c918189b..ad653482 100644 --- a/src/routes/open/components/ReviewInformation/index.tsx +++ b/src/routes/open/components/ReviewInformation/index.tsx @@ -1,5 +1,5 @@ import TableContainer from '@material-ui/core/TableContainer' -import { withStyles } from '@material-ui/core/styles' +import { createStyles, makeStyles } from '@material-ui/core/styles' import classNames from 'classnames' import * as React from 'react' @@ -22,7 +22,7 @@ import { background, border, lg, screenSm, sm } from 'src/theme/variables' const { useEffect, useState } = React -const styles = () => ({ +const styles = createStyles({ root: { minHeight: '300px', [`@media (min-width: ${screenSm}px)`]: { @@ -84,7 +84,15 @@ const styles = () => ({ }, }) -const ReviewComponent = ({ classes, userAccount, values }: any) => { +const useStyles = makeStyles(styles) + +type ReviewComponentProps = { + userAccount: string + values: any +} + +const ReviewComponent = ({ userAccount, values }: ReviewComponentProps) => { + const classes = useStyles() const [gasCosts, setGasCosts] = useState('< 0.001') const names = getNamesFrom(values) const addresses = getAccountsFrom(values) @@ -198,12 +206,11 @@ const ReviewComponent = ({ classes, userAccount, values }: any) => { ) } -const ReviewPage = withStyles(styles as any)(ReviewComponent) - -const Review = () => (controls, { values }) => ( +// eslint-disable-next-line react/display-name +const Review = () => (controls, props): React.ReactElement => ( <> - + ) diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/index.tsx b/src/routes/open/components/SafeOwnersConfirmationsForm/index.tsx index da0e67c4..6951f19a 100644 --- a/src/routes/open/components/SafeOwnersConfirmationsForm/index.tsx +++ b/src/routes/open/components/SafeOwnersConfirmationsForm/index.tsx @@ -1,10 +1,8 @@ import InputAdornment from '@material-ui/core/InputAdornment' import MenuItem from '@material-ui/core/MenuItem' -import { withStyles } from '@material-ui/core/styles' +import { makeStyles } from '@material-ui/core/styles' import CheckCircle from '@material-ui/icons/CheckCircle' import * as React from 'react' -import { withRouter } from 'react-router-dom' - import { styles } from './style' import { getAddressValidator } from './validators' @@ -38,6 +36,9 @@ import { getOwnerNameBy, } from 'src/routes/open/components/fields' import { getAccountsFrom } from 'src/routes/open/utils/safeDataExtractor' +import { useSelector } from 'react-redux' +import { addressBookSelector } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBook } from 'src/logic/addressBook/utils' const { useState } = React @@ -63,10 +64,14 @@ export const calculateValuesAfterRemoving = (index, notRemovedOwners, values) => return initialValues } -const SafeOwners = (props) => { - const { classes, errors, form, otherAccounts, values } = props +const useStyles = makeStyles(styles) + +const SafeOwnersForm = (props): React.ReactElement => { + const { errors, form, otherAccounts, values } = props + const classes = useStyles() const validOwners = getNumOwnersFrom(values) + const addressBook = useSelector(addressBookSelector) const [numOwners, setNumOwners] = useState(validOwners) const [qrModalOpen, setQrModalOpen] = useState(false) @@ -125,6 +130,7 @@ const SafeOwners = (props) => { {[...Array(Number(numOwners))].map((x, index) => { const addressName = getOwnerAddressBy(index) + const ownerName = getOwnerNameBy(index) return ( @@ -132,7 +138,7 @@ const SafeOwners = (props) => { { { - form.mutators.setValue(addressName, val) + fieldMutator={(newOwnerAddress) => { + const newOwnerName = getNameFromAddressBook(addressBook, newOwnerAddress, { + filterOnlyValidName: true, + }) + form.mutators.setValue(addressName, newOwnerAddress) + if (newOwnerName) { + form.mutators.setValue(ownerName, newOwnerName) + } }} // eslint-disable-next-line // @ts-ignore @@ -224,8 +236,6 @@ const SafeOwners = (props) => { ) } -const SafeOwnersForm = withStyles(styles as any)(withRouter(SafeOwners)) - const SafeOwnersPage = ({ updateInitialProps }) => function OpenSafeOwnersPage(controls, { errors, form, values }) { return ( diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/style.ts b/src/routes/open/components/SafeOwnersConfirmationsForm/style.ts index 1e4e6a3b..61c73caf 100644 --- a/src/routes/open/components/SafeOwnersConfirmationsForm/style.ts +++ b/src/routes/open/components/SafeOwnersConfirmationsForm/style.ts @@ -1,6 +1,7 @@ import { disabled, extraSmallFontSize, lg, md, screenSm, sm } from 'src/theme/variables' +import { createStyles } from '@material-ui/core' -export const styles = () => ({ +export const styles = createStyles({ root: { display: 'flex', }, diff --git a/src/routes/open/container/Open.tsx b/src/routes/open/container/Open.tsx index 3865e166..360d3884 100644 --- a/src/routes/open/container/Open.tsx +++ b/src/routes/open/container/Open.tsx @@ -2,15 +2,9 @@ import { Loader } from '@gnosis.pm/safe-react-components' import queryString from 'query-string' import React, { useEffect, useState } from 'react' import ReactGA from 'react-ga' -import { connect } from 'react-redux' -import { withRouter, RouteComponentProps } from 'react-router-dom' - -import Opening from '../../opening' -import Layout from '../components/Layout' - -import actions from './actions' -import selector from './selector' - +import { useDispatch, useSelector } from 'react-redux' +import Opening from 'src/routes/opening' +import Layout from 'src/routes/open/components/Layout' import Page from 'src/components/layout/Page' import { getSafeDeploymentTransaction } from 'src/logic/contracts/safeContracts' import { checkReceiptStatus } from 'src/logic/wallets/ethTransactions' @@ -25,6 +19,9 @@ import { SAFELIST_ADDRESS, WELCOME_ADDRESS } from 'src/routes/routes' import { buildSafe } from 'src/logic/safe/store/actions/fetchSafe' import { history } from 'src/store' import { loadFromStorage, removeFromStorage, saveToStorage } from 'src/utils/storage' +import { userAccountSelector } from 'src/logic/wallets/store/selectors' +import { SafeRecordProps } from 'src/logic/safe/store/models/safe' +import { addOrUpdateSafe } from 'src/logic/safe/store/actions/addOrUpdateSafe' const SAFE_PENDING_CREATION_STORAGE_KEY = 'SAFE_PENDING_CREATION_STORAGE_KEY' @@ -39,13 +36,15 @@ const validateQueryParams = (ownerAddresses, ownerNames, threshold, safeName) => if (Number.isNaN(Number(threshold))) { return false } - if (threshold > ownerAddresses.length) { - return false - } - return true + return threshold <= ownerAddresses.length } -export const getSafeProps = async (safeAddress, safeName, ownersNames, ownerAddresses) => { +export const getSafeProps = async ( + safeAddress: string, + safeName: string, + ownersNames: string[], + ownerAddresses: string[], +): Promise => { const safeProps = await buildSafe(safeAddress, safeName) const owners = getOwnersFrom(ownersNames, ownerAddresses) safeProps.owners = owners @@ -81,19 +80,14 @@ export const createSafe = (values, userAccount) => { return promiEvent } -interface OwnProps extends RouteComponentProps { - userAccount: string - network: string - provider: string - addSafe: any -} - -const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.ReactElement => { +const Open = (): React.ReactElement => { const [loading, setLoading] = useState(false) const [showProgress, setShowProgress] = useState(false) const [creationTxPromise, setCreationTxPromise] = useState() const [safeCreationPendingInfo, setSafeCreationPendingInfo] = useState() const [safePropsFromUrl, setSafePropsFromUrl] = useState() + const userAccount = useSelector(userAccountSelector) + const dispatch = useDispatch() useEffect(() => { // #122: Allow to migrate an old Multisig by passing the parameters to the URL. @@ -141,14 +135,15 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac setShowProgress(true) } - const onSafeCreated = async (safeAddress) => { + const onSafeCreated = async (safeAddress): Promise => { const pendingCreation = await loadFromStorage<{ txHash: string }>(SAFE_PENDING_CREATION_STORAGE_KEY) const name = getSafeNameFrom(pendingCreation) const ownersNames = getNamesFrom(pendingCreation) const ownerAddresses = getAccountsFrom(pendingCreation) const safeProps = await getSafeProps(safeAddress, name, ownersNames, ownerAddresses) - addSafe(safeProps) + + await dispatch(addOrUpdateSafe(safeProps)) ReactGA.event({ category: 'User', @@ -194,21 +189,14 @@ const Open = ({ addSafe, network, provider, userAccount }: OwnProps): React.Reac creationTxHash={safeCreationPendingInfo?.txHash} onCancel={onCancel} onRetry={onRetry} - onSuccess={onSafeCreated as any} - provider={provider} + onSuccess={onSafeCreated} submittedPromise={creationTxPromise} /> ) : ( - + )} ) } -export default connect(selector, actions)(withRouter(Open)) +export default Open diff --git a/src/routes/open/container/actions.ts b/src/routes/open/container/actions.ts deleted file mode 100644 index 918dbf44..00000000 --- a/src/routes/open/container/actions.ts +++ /dev/null @@ -1,5 +0,0 @@ -import addSafe from 'src/logic/safe/store/actions/addSafe' - -export default { - addSafe, -} diff --git a/src/routes/open/container/selector.ts b/src/routes/open/container/selector.ts deleted file mode 100644 index c0163f10..00000000 --- a/src/routes/open/container/selector.ts +++ /dev/null @@ -1,9 +0,0 @@ -import { createStructuredSelector } from 'reselect' - -import { networkSelector, providerNameSelector, userAccountSelector } from 'src/logic/wallets/store/selectors' - -export default createStructuredSelector({ - provider: providerNameSelector, - network: networkSelector, - userAccount: userAccountSelector, -}) diff --git a/src/routes/opening/index.tsx b/src/routes/opening/index.tsx index efe99000..d9762846 100644 --- a/src/routes/opening/index.tsx +++ b/src/routes/opening/index.tsx @@ -2,7 +2,7 @@ import { Loader, Stepper } from '@gnosis.pm/safe-react-components' import React, { useEffect, useState } from 'react' import styled from 'styled-components' -import { ErrorFooter } from './components/Footer' +import { ErrorFooter } from 'src/routes/opening/components/Footer' import { isConfirmationStep, steps } from './steps' import Button from 'src/components/layout/Button' @@ -13,6 +13,8 @@ import { initContracts } from 'src/logic/contracts/safeContracts' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { getWeb3 } from 'src/logic/wallets/getWeb3' import { background, connected } from 'src/theme/variables' +import { providerNameSelector } from 'src/logic/wallets/store/selectors' +import { useSelector } from 'react-redux' const loaderDotsSvg = require('./assets/loader-dots.svg') const successSvg = require('./assets/success.svg') @@ -102,16 +104,17 @@ const BackButton = styled(Button)` // onCancel: () => void // } -const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, provider, submittedPromise }: any) => { +const SafeDeployment = ({ creationTxHash, onCancel, onRetry, onSuccess, submittedPromise }): React.ReactElement => { const [loading, setLoading] = useState(true) const [stepIndex, setStepIndex] = useState(0) const [safeCreationTxHash, setSafeCreationTxHash] = useState('') - const [createdSafeAddress, setCreatedSafeAddress] = useState() + const [createdSafeAddress, setCreatedSafeAddress] = useState('') const [error, setError] = useState(false) const [intervalStarted, setIntervalStarted] = useState(false) const [waitingSafeDeployed, setWaitingSafeDeployed] = useState(false) const [continueButtonDisabled, setContinueButtonDisabled] = useState(false) + const provider = useSelector(providerNameSelector) const confirmationStep = isConfirmationStep(stepIndex) diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx index f0a9b418..883690a8 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.tsx @@ -16,6 +16,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction' import { safeOwnersSelector, safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' import { checksumAddress } from 'src/utils/checksumAddress' +import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' const styles = () => ({ biggerModalWindow: { @@ -92,7 +93,7 @@ const AddOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose }) try { await sendAddOwner(values, safeAddress, owners, enqueueSnackbar, closeSnackbar, dispatch) dispatch( - addOrUpdateAddressBookEntry(values.ownerAddress, { name: values.ownerName, address: values.ownerAddress }), + addOrUpdateAddressBookEntry(makeAddressBookEntry({ name: values.ownerName, address: values.ownerAddress })), ) } catch (error) { console.error('Error while removing an owner', error) diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx index 1066ed88..8b743074 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/index.tsx @@ -14,6 +14,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction' import replaceSafeOwner from 'src/logic/safe/store/actions/replaceSafeOwner' import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' import { checksumAddress } from 'src/utils/checksumAddress' +import { makeAddressBookEntry } from 'src/logic/addressBook/model/addressBook' const styles = () => ({ biggerModalWindow: { @@ -96,10 +97,7 @@ const ReplaceOwner = ({ classes, closeSnackbar, enqueueSnackbar, isOpen, onClose await sendReplaceOwner(values, safeAddress, ownerAddress, enqueueSnackbar, closeSnackbar, threshold, dispatch) dispatch( - // Needs the `address` field because we need to provide the minimum required values to ADD a new entry - // The reducer will update all the addressBooks stored, so we cannot decide what to do beforehand, - // thus, we pass the minimum required fields (name and address) - addOrUpdateAddressBookEntry(values.ownerAddress, { name: values.ownerName, address: values.ownerAddress }), + addOrUpdateAddressBookEntry(makeAddressBookEntry({ address: values.ownerAddress, name: values.ownerName })), ) } catch (error) { console.error('Error while removing an owner', error) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx index 31320d5d..bab38466 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/IncomingTxDescription/index.tsx @@ -5,7 +5,7 @@ import { useSelector } from 'react-redux' import EtherscanLink from 'src/components/EtherscanLink' import Block from 'src/components/layout/Block' import Bold from 'src/components/layout/Bold' -import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' import { getIncomingTxAmount } from 'src/routes/safe/components/Transactions/TxsTable/columns' import { lg, md } from 'src/theme/variables' @@ -35,7 +35,7 @@ const TransferDescription = ({ from, txFromName, value = '' }) => ( const IncomingTxDescription = ({ tx }) => { const classes = useStyles() - const txFromName = useSelector((state) => getNameFromAddressBook(state, tx.from)) + const txFromName = useSelector((state) => getNameFromAddressBookSelector(state, tx.from)) return ( diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx index fc0c8c4b..8ba79a83 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/OwnerComponent.tsx @@ -16,7 +16,7 @@ import { styles } from './style' import Block from 'src/components/layout/Block' import Button from 'src/components/layout/Button' import Img from 'src/components/layout/Img' -import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import { OwnersWithoutConfirmations } from './index' export const CONFIRM_TX_BTN_TEST_ID = 'confirm-btn' @@ -64,7 +64,7 @@ const OwnerComponent = (props: OwnerComponentProps): React.ReactElement => { showExecuteRejectBtn, confirmed, } = props - const nameInAdbk = useSelector((state) => getNameFromAddressBook(state, owner)) + const nameInAdbk = useSelector((state) => getNameFromAddressBookSelector(state, owner)) const classes = useStyles() const [imgCircle, setImgCircle] = React.useState(ConfirmSmallGreyCircle) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx index 489451af..0ed87bec 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/CustomDescription.tsx @@ -15,7 +15,7 @@ import Bold from 'src/components/layout/Bold' import { humanReadableValue } from 'src/logic/tokens/utils/humanReadableValue' import Collapse from 'src/components/Collapse' import { useSelector } from 'react-redux' -import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import Paragraph from 'src/components/layout/Paragraph' import LinkWithRef from 'src/components/layout/Link' import { shortVersionOf } from 'src/logic/wallets/ethAddresses' @@ -176,7 +176,7 @@ interface GenericCustomDataProps { const GenericCustomData = ({ amount = '0', data, recipient, storedTx }: GenericCustomDataProps): React.ReactElement => { const classes = useStyles() - const recipientName = useSelector((state) => getNameFromAddressBook(state, recipient)) + const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient)) return ( diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx index 974382b9..596f3224 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/SettingsDescription.tsx @@ -1,7 +1,7 @@ import { useSelector } from 'react-redux' import React from 'react' -import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import Block from 'src/components/layout/Block' import Bold from 'src/components/layout/Bold' import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' @@ -21,7 +21,7 @@ interface RemovedOwnerProps { } const RemovedOwner = ({ removedOwner }: RemovedOwnerProps): React.ReactElement => { - const ownerChangedName = useSelector((state) => getNameFromAddressBook(state, removedOwner)) + const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, removedOwner)) return ( @@ -40,7 +40,7 @@ interface AddedOwnerProps { } const AddedOwner = ({ addedOwner }: AddedOwnerProps): React.ReactElement => { - const ownerChangedName = useSelector((state) => getNameFromAddressBook(state, addedOwner)) + const ownerChangedName = useSelector((state) => getNameFromAddressBookSelector(state, addedOwner)) return ( diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx index 2e206a1f..4fc0be97 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/TxDescription/TransferDescription.tsx @@ -2,7 +2,7 @@ import React from 'react' import { useSelector } from 'react-redux' import { TRANSACTIONS_DESC_SEND_TEST_ID } from './index' -import { getNameFromAddressBook } from 'src/logic/addressBook/store/selectors' +import { getNameFromAddressBookSelector } from 'src/logic/addressBook/store/selectors' import Block from 'src/components/layout/Block' import Bold from 'src/components/layout/Bold' import OwnerAddressTableCell from 'src/routes/safe/components/Settings/ManageOwners/OwnerAddressTableCell' @@ -14,7 +14,7 @@ interface TransferDescriptionProps { } const TransferDescription = ({ amount = '', recipient }: TransferDescriptionProps): React.ReactElement => { - const recipientName = useSelector((state) => getNameFromAddressBook(state, recipient)) + const recipientName = useSelector((state) => getNameFromAddressBookSelector(state, recipient)) return ( Send {amount} to: From 32547a8ae6ca395104ed3c5978328e0329ecea58 Mon Sep 17 00:00:00 2001 From: nicolas Date: Wed, 23 Sep 2020 15:34:35 -0300 Subject: [PATCH 11/11] Add WalletConnect safe-app (#1364) * Add safe-connect safe-app * rename safe-connect to walletConnect Co-authored-by: Daniel Sanchez --- src/routes/safe/components/Apps/utils.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/routes/safe/components/Apps/utils.ts b/src/routes/safe/components/Apps/utils.ts index e5dae153..beeba0c0 100644 --- a/src/routes/safe/components/Apps/utils.ts +++ b/src/routes/safe/components/Apps/utils.ts @@ -38,6 +38,8 @@ export const staticAppsList: Array<{ url: string; disabled: boolean }> = [ { url: `${process.env.REACT_APP_IPFS_GATEWAY}/QmQovvfYYMUXjZfNbysQDUEXR8nr55iJRwcYgJQGJR7KEA`, disabled: false }, // TX-Builder { url: `${gnosisAppsUrl}/tx-builder`, disabled: false }, + // Wallet-Connect + { url: `${gnosisAppsUrl}/walletConnect`, disabled: false }, // Yearn Vaults { url: `${process.env.REACT_APP_IPFS_GATEWAY}/Qme9HuPPhgCtgfj1CktvaDKhTesMueGCV2Kui1Sqna3Xs9`, disabled: false }, ]