From 0378470ba6e679c06d16398378863006efacbc21 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 15:01:11 +0400 Subject: [PATCH 01/42] Fix missing whitespace in required confirmation text --- .../ManageOwners/AddOwnerModal/screens/Review/index.jsx | 7 +------ .../RemoveOwnerModal/screens/Review/index.jsx | 7 +------ .../ReplaceOwnerModal/screens/Review/index.jsx | 8 +------- .../Settings/ThresholdSettings/ChangeThreshold/index.jsx | 8 ++------ 4 files changed, 5 insertions(+), 25 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx index ec6b9054..70f67fe0 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx @@ -76,12 +76,7 @@ const ReviewAddOwner = ({ Any transaction requires the confirmation of: - {values.threshold} - {' '} - out of - {owners.size + 1} - {' '} - owner(s) + {`${values.threshold} out of ${owners.size + 1} owner(s)`} diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx index 1f29c38b..4acb5f6a 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx @@ -87,12 +87,7 @@ const ReviewRemoveOwner = ({ Any transaction requires the confirmation of: - {values.threshold} - {' '} - out of - {owners.size - 1} - {' '} - owner(s) + {`${values.threshold} out of ${owners.size - 1} owner(s)`} diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx index 0a956687..20e5c306 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx @@ -89,13 +89,7 @@ const ReviewRemoveOwner = ({ Any transaction requires the confirmation of: - {threshold} - {' '} - out of - {' '} - {owners.size} - {' '} - owner(s) + {`${threshold} out of ${owners.size} owner(s)`} diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx index e980444a..c7a015f4 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx @@ -70,7 +70,7 @@ const ChangeThreshold = ({ ( + render={(props: Object) => ( <> {[...Array(Number(owners.size))].map((x, index) => ( @@ -92,11 +92,7 @@ const ChangeThreshold = ({ - out of - {' '} - {owners.size} - {' '} -owner(s) + {`out of ${owners.size} owner(s)`} From c38325bca59b74ecab66c6f668001e73914cd16f Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 15:37:35 +0400 Subject: [PATCH 02/42] Update sending funds with threshold > 2 test because 'approve and execute' is pre-checked now --- package.json | 4 +- .../ExpandedTx/ApproveTxModal/index.jsx | 6 +- src/test/safe.dom.funds.threshold>1.test.js | 9 +- yarn.lock | 140 ++---------------- 4 files changed, 17 insertions(+), 142 deletions(-) diff --git a/package.json b/package.json index e000990a..c0bc890d 100644 --- a/package.json +++ b/package.json @@ -108,13 +108,13 @@ "classnames": "^2.2.6", "css-loader": "3.2.0", "detect-port": "^1.3.0", - "eslint": "6.5.1", + "eslint": "5.16.0", "eslint-config-airbnb": "18.0.1", "eslint-plugin-flowtype": "4.3.0", "eslint-plugin-import": "2.18.2", "eslint-plugin-jest": "22.17.0", "eslint-plugin-jsx-a11y": "6.2.3", - "eslint-plugin-react": "7.15.0", + "eslint-plugin-react": "7.14.3", "ethereumjs-abi": "0.6.8", "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "4.2.0", diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx index aeba3ae7..b4e12d7d 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx @@ -61,7 +61,7 @@ const ApproveTxModal = ({ enqueueSnackbar, closeSnackbar, }: Props) => { - const [approveAndExecute, setApproveAndExecute] = useState(false) + const [approveAndExecute, setApproveAndExecute] = useState(true) const { title, description } = getModalTitleAndDescription(thresholdReached) const oneConfirmationLeft = tx.confirmations.size + 1 === threshold @@ -102,8 +102,8 @@ const ApproveTxModal = ({ {!thresholdReached && oneConfirmationLeft && ( <> - Approving transaction does not execute it immediately. If you want to approve and execute the - transaction right away, click on checkbox below. + Approving this transaction executes it right away. If you want approve but execute the transaction + manually later, click on the checkbox below. } diff --git a/src/test/safe.dom.funds.threshold>1.test.js b/src/test/safe.dom.funds.threshold>1.test.js index 25ab2ded..9f4aaf77 100644 --- a/src/test/safe.dom.funds.threshold>1.test.js +++ b/src/test/safe.dom.funds.threshold>1.test.js @@ -14,7 +14,6 @@ import { TRANSACTION_ROW_TEST_ID } from '~/routes/safe/components/Transactions/T import { useTestAccountAt, resetTestAccount } from './utils/accounts' import { CONFIRM_TX_BTN_TEST_ID, - EXECUTE_TX_BTN_TEST_ID, } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/ButtonRow' import { APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID } from '~/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal' @@ -67,13 +66,7 @@ describe('DOM > Feature > Sending Funds', () => { const approveTxBtn = await waitForElement(() => SafeDom.getByTestId(APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID)) fireEvent.click(approveTxBtn) - // EXECUTE TX - const executeTxBtn = await waitForElement(() => SafeDom.getByTestId(EXECUTE_TX_BTN_TEST_ID)) - fireEvent.click(executeTxBtn) - - const confirmReviewTxBtn = await waitForElement(() => SafeDom.getByTestId(APPROVE_TX_MODAL_SUBMIT_BTN_TEST_ID)) - fireEvent.click(confirmReviewTxBtn) - await sleep(500) + await sleep(1000) // THEN const safeFunds = await getBalanceInEtherOf(safeAddress) diff --git a/yarn.lock b/yarn.lock index 6f4a36d6..6163fbe6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3044,11 +3044,6 @@ acorn-jsx@^5.0.0: resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.1.tgz#32a064fd925429216a09b141102bfdd185fae40e" integrity sha512-HJ7CfNHrfJLlNTzIEUTj43LNWGkqpRLxm3YjAlcD0ACydk9XynzYsCBHxut+iqt+1aBXkx9UP/w/ZqMr13XIzg== -acorn-jsx@^5.0.2: - version "5.0.2" - resolved "https://registry.yarnpkg.com/acorn-jsx/-/acorn-jsx-5.0.2.tgz#84b68ea44b373c4f8686023a551f61a21b7c4a4f" - integrity sha512-tiNTrP1MP0QrChmD2DdupCr6HWSFeKVw5d/dHTu4Y7rkAkRhU/Dt7dphAfIUyxtHpl/eBVip5uTNSpQJHylpAw== - acorn-walk@^6.0.1, acorn-walk@^6.1.1: version "6.2.0" resolved "https://registry.yarnpkg.com/acorn-walk/-/acorn-walk-6.2.0.tgz#123cb8f3b84c2171f1f7fb252615b1c78a6b1a8c" @@ -3074,11 +3069,6 @@ acorn@^6.0.1, acorn@^6.0.7, acorn@^6.2.0, acorn@^6.2.1: resolved "https://registry.yarnpkg.com/acorn/-/acorn-6.2.1.tgz#3ed8422d6dec09e6121cc7a843ca86a330a86b51" integrity sha512-JD0xT5FCRDNyjDda3Lrg/IxFscp9q4tiYtxE1/nOzlKCk7hIRuYjhq1kCNkbPjMRMZuFq20HNQn1I9k8Oj0E+Q== -acorn@^7.0.0: - version "7.0.0" - resolved "https://registry.yarnpkg.com/acorn/-/acorn-7.0.0.tgz#26b8d1cd9a9b700350b71c0905546f64d1284e7a" - integrity sha512-PaF/MduxijYYt7unVGRuds1vBC9bFxbNf+VWqhOClfdgy7RlVkQqt610ig1/yxTgsDIfW1cWDel5EBbOy3jdtQ== - address@1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/address/-/address-1.0.3.tgz#b5f50631f8d6cec8bd20c963963afb55e06cbce9" @@ -7345,20 +7335,20 @@ eslint-plugin-jsx-a11y@6.2.3: has "^1.0.3" jsx-ast-utils "^2.2.1" -eslint-plugin-react@7.15.0: - version "7.15.0" - resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.15.0.tgz#4808b19cf7b4c439454099d4eb8f0cf0e9fe31dd" - integrity sha512-NbIh/yVXoltm8Df28PiPRanfCZAYubGqXU391MTCpW955Vum7S0nZdQYXGAvDh9ye4aNCmOR6YcYZsfMbEQZQA== +eslint-plugin-react@7.14.3: + version "7.14.3" + resolved "https://registry.yarnpkg.com/eslint-plugin-react/-/eslint-plugin-react-7.14.3.tgz#911030dd7e98ba49e1b2208599571846a66bdf13" + integrity sha512-EzdyyBWC4Uz2hPYBiEJrKCUi2Fn+BJ9B/pJQcjw5X+x/H2Nm59S4MJIvL4O5NEE0+WbnQwEBxWY03oUk+Bc3FA== dependencies: array-includes "^3.0.3" doctrine "^2.1.0" has "^1.0.3" - jsx-ast-utils "^2.2.1" + jsx-ast-utils "^2.1.0" object.entries "^1.1.0" object.fromentries "^2.0.0" object.values "^1.1.0" prop-types "^15.7.2" - resolve "^1.12.0" + resolve "^1.10.1" eslint-scope@^3.7.1: version "3.7.3" @@ -7376,14 +7366,6 @@ eslint-scope@^4.0.0, eslint-scope@^4.0.3: esrecurse "^4.1.0" estraverse "^4.1.1" -eslint-scope@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-5.0.0.tgz#e87c8887c73e8d1ec84f1ca591645c358bfc8fb9" - integrity sha512-oYrhJW7S0bxAFDvWqzvMPRm6pcgcnWc4QnofCAqRTRfQC0JcwenzGglTtsLyIuuWFfkqDG9vz67cnttSd53djw== - dependencies: - esrecurse "^4.1.0" - estraverse "^4.1.1" - eslint-utils@^1.3.1: version "1.4.0" resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.0.tgz#e2c3c8dba768425f897cf0f9e51fe2e241485d4c" @@ -7391,67 +7373,12 @@ eslint-utils@^1.3.1: dependencies: eslint-visitor-keys "^1.0.0" -eslint-utils@^1.4.2: - version "1.4.2" - resolved "https://registry.yarnpkg.com/eslint-utils/-/eslint-utils-1.4.2.tgz#166a5180ef6ab7eb462f162fd0e6f2463d7309ab" - integrity sha512-eAZS2sEUMlIeCjBeubdj45dmBHQwPHWyBcT1VSYB7o9x9WRRqKxyUoiXlRjyAwzN7YEzHJlYg0NmzDRWx6GP4Q== - dependencies: - eslint-visitor-keys "^1.0.0" - eslint-visitor-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.0.0.tgz#3f3180fb2e291017716acb4c9d6d5b5c34a6a81d" integrity sha512-qzm/XxIbxm/FHyH341ZrbnMUpe+5Bocte9xkmFMzPMjRaZMcXww+MpBptFvtU+79L362nqiLhekCxCxDPaUMBQ== -eslint-visitor-keys@^1.1.0: - version "1.1.0" - resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" - integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== - -eslint@6.5.1: - version "6.5.1" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-6.5.1.tgz#828e4c469697d43bb586144be152198b91e96ed6" - integrity sha512-32h99BoLYStT1iq1v2P9uwpyznQ4M2jRiFB6acitKz52Gqn+vPaMDUTB1bYi1WN4Nquj2w+t+bimYUG83DC55A== - dependencies: - "@babel/code-frame" "^7.0.0" - ajv "^6.10.0" - chalk "^2.1.0" - cross-spawn "^6.0.5" - debug "^4.0.1" - doctrine "^3.0.0" - eslint-scope "^5.0.0" - eslint-utils "^1.4.2" - eslint-visitor-keys "^1.1.0" - espree "^6.1.1" - esquery "^1.0.1" - esutils "^2.0.2" - file-entry-cache "^5.0.1" - functional-red-black-tree "^1.0.1" - glob-parent "^5.0.0" - globals "^11.7.0" - ignore "^4.0.6" - import-fresh "^3.0.0" - imurmurhash "^0.1.4" - inquirer "^6.4.1" - is-glob "^4.0.0" - js-yaml "^3.13.1" - json-stable-stringify-without-jsonify "^1.0.1" - levn "^0.3.0" - lodash "^4.17.14" - minimatch "^3.0.4" - mkdirp "^0.5.1" - natural-compare "^1.4.0" - optionator "^0.8.2" - progress "^2.0.0" - regexpp "^2.0.1" - semver "^6.1.2" - strip-ansi "^5.2.0" - strip-json-comments "^3.0.1" - table "^5.2.3" - text-table "^0.2.0" - v8-compile-cache "^2.0.3" - -eslint@^5.0.0, eslint@^5.5.0: +eslint@5.16.0, eslint@^5.0.0, eslint@^5.5.0: version "5.16.0" resolved "https://registry.yarnpkg.com/eslint/-/eslint-5.16.0.tgz#a1e3ac1aae4a3fbd8296fcf8f7ab7314cbb6abea" integrity sha512-S3Rz11i7c8AA5JPv7xAH+dOyq/Cu/VXHiHXBPOU1k/JAM5dXqQPt3qcrhpHSorXmrpu2g0gkIBVXAqCpzfoZIg== @@ -7510,15 +7437,6 @@ espree@^5.0.1: acorn-jsx "^5.0.0" eslint-visitor-keys "^1.0.0" -espree@^6.1.1: - version "6.1.1" - resolved "https://registry.yarnpkg.com/espree/-/espree-6.1.1.tgz#7f80e5f7257fc47db450022d723e356daeb1e5de" - integrity sha512-EYbr8XZUhWbYCqQRW0duU5LxzL5bETN6AjKBGy1302qqzPaCH10QbRg3Wvco79Z8x9WbiE8HYB4e75xl6qUYvQ== - dependencies: - acorn "^7.0.0" - acorn-jsx "^5.0.2" - eslint-visitor-keys "^1.1.0" - esprima@^3.1.3, esprima@~3.1.0: version "3.1.3" resolved "https://registry.yarnpkg.com/esprima/-/esprima-3.1.3.tgz#fdca51cee6133895e3c88d535ce49dbff62a4633" @@ -9093,13 +9011,6 @@ glob-parent@^3.1.0: is-glob "^3.1.0" path-dirname "^1.0.0" -glob-parent@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/glob-parent/-/glob-parent-5.0.0.tgz#1dc99f0f39b006d3e92c2c284068382f0c20e954" - integrity sha512-Z2RwiujPRGluePM6j699ktJYxmPpJKCfpGA13jz2hmFZC7gKetzrWvg5KN3+OsIFmydGyZ1AVwERCq1w/ZZwRg== - 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" @@ -10097,25 +10008,6 @@ inquirer@^6.2.0, inquirer@^6.2.2: strip-ansi "^5.1.0" through "^2.3.6" -inquirer@^6.4.1: - version "6.5.2" - resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-6.5.2.tgz#ad50942375d036d327ff528c08bd5fab089928ca" - integrity sha512-cntlB5ghuB0iuO65Ovoi8ogLHiWGs/5yNrtUcKjFhSSiVeAIVpD7koaSU9RM8mpXw5YDi9RdYXGQMaOURB7ycQ== - dependencies: - ansi-escapes "^3.2.0" - chalk "^2.4.2" - cli-cursor "^2.1.0" - cli-width "^2.0.0" - external-editor "^3.0.3" - figures "^2.0.0" - lodash "^4.17.12" - mute-stream "0.0.7" - run-async "^2.2.0" - rxjs "^6.4.0" - string-width "^2.1.0" - strip-ansi "^5.1.0" - through "^2.3.6" - internal-ip@^4.3.0: version "4.3.0" resolved "https://registry.yarnpkg.com/internal-ip/-/internal-ip-4.3.0.tgz#845452baad9d2ca3b69c635a137acb9a0dad0907" @@ -10406,7 +10298,7 @@ is-glob@^3.1.0: dependencies: is-extglob "^2.1.0" -is-glob@^4.0.0, is-glob@^4.0.1: +is-glob@^4.0.0: version "4.0.1" resolved "https://registry.yarnpkg.com/is-glob/-/is-glob-4.0.1.tgz#7567dbe9f2f5e2467bc77ab83c4a29482407a5dc" integrity sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg== @@ -11467,7 +11359,7 @@ jss@10.0.0-alpha.25: is-in-browser "^1.1.3" tiny-warning "^1.0.2" -jsx-ast-utils@^2.2.1: +jsx-ast-utils@^2.1.0, jsx-ast-utils@^2.2.1: version "2.2.1" resolved "https://registry.yarnpkg.com/jsx-ast-utils/-/jsx-ast-utils-2.2.1.tgz#4d4973ebf8b9d2837ee91a8208cc66f3a2776cfb" integrity sha512-v3FxCcAf20DayI+uxnCuw795+oOIkVu6EnJ1+kSzhqqTZHNkTZ7B66ZgLp4oLJ/gbA64cI0B7WRoHZMSRdyVRQ== @@ -15700,7 +15592,7 @@ resolve@^1.1.6, resolve@^1.1.7, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.3.2 dependencies: path-parse "^1.0.6" -resolve@^1.12.0: +resolve@^1.10.1, resolve@^1.12.0: version "1.12.0" resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.12.0.tgz#3fc644a35c84a48554609ff26ec52b66fa577df6" integrity sha512-B/dOmuoAik5bKcD6s6nXDCjzUKnaDvdkRyAk6rsmsKLipWj4797iothd7jmmUhWTfinVMU+wc56rYKsit2Qy4w== @@ -16087,7 +15979,7 @@ semver@6.2.0, semver@^6.0.0, semver@^6.1.1: resolved "https://registry.yarnpkg.com/semver/-/semver-6.2.0.tgz#4d813d9590aaf8a9192693d6c85b9344de5901db" integrity sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A== -semver@^6.1.2, semver@^6.2.0, semver@^6.3.0: +semver@^6.2.0, semver@^6.3.0: version "6.3.0" resolved "https://registry.yarnpkg.com/semver/-/semver-6.3.0.tgz#ee0a64c8af5e8ceea67687b133761e1becbd1d3d" integrity sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw== @@ -16888,11 +16780,6 @@ strip-json-comments@^2.0.1, strip-json-comments@~2.0.1: resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-2.0.1.tgz#3c531942e908c2697c0ec344858c286c7ca0a60a" integrity sha1-PFMZQukIwml8DsNEhYwobHygpgo= -strip-json-comments@^3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/strip-json-comments/-/strip-json-comments-3.0.1.tgz#85713975a91fb87bf1b305cca77395e40d2a64a7" - integrity sha512-VTyMAUfdm047mwKl+u79WIdrZxtFtn+nBxHeb844XBQ9uMNTuTHdx2hc5RiAJYqwTj3wc/xe5HLSdJSkJ+WfZw== - style-loader@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/style-loader/-/style-loader-1.0.0.tgz#1d5296f9165e8e2c85d24eee0b7caf9ec8ca1f82" @@ -18486,11 +18373,6 @@ v8-compile-cache@2.0.3: resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.0.3.tgz#00f7494d2ae2b688cfe2899df6ed2c54bef91dbe" integrity sha512-CNmdbwQMBjwr9Gsmohvm0pbL954tJrNzf6gWL3K+QMQf00PF7ERGrEiLgjuU3mKreLC2MeGhUsNV9ybTbLgd3w== -v8-compile-cache@^2.0.3: - version "2.1.0" - resolved "https://registry.yarnpkg.com/v8-compile-cache/-/v8-compile-cache-2.1.0.tgz#e14de37b31a6d194f5690d67efc4e7f6fc6ab30e" - integrity sha512-usZBT3PW+LOjM25wbqIlZwPeJV+3OSz3M1k1Ws8snlW39dZyYL9lOGC5FgPVHfk0jKmjiDV8Z0mIbVQPiwFs7g== - v8flags@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/v8flags/-/v8flags-2.1.1.tgz#aab1a1fa30d45f88dd321148875ac02c0b55e5b4" From a113f2321dd0a9a99abe1e2387e7e94900bc6068 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 16:16:53 +0400 Subject: [PATCH 03/42] add qr scanner to send funds/send custom tx flows --- .../ScanQRModal/index.jsx | 0 .../ScanQRModal/style.js | 0 .../ScanQRModal/utils.js | 0 .../SafeOwnersConfirmationsForm/index.jsx | 2 +- .../SendModal/screens/SendCustomTx/index.jsx | 50 ++++++++++++--- .../SendModal/screens/SendCustomTx/style.js | 3 + .../SendModal/screens/SendFunds/index.jsx | 64 +++++++++++++++---- .../SendModal/screens/SendFunds/style.js | 3 + 8 files changed, 98 insertions(+), 24 deletions(-) rename src/{routes/open/components/SafeOwnersConfirmationsForm => components}/ScanQRModal/index.jsx (100%) rename src/{routes/open/components/SafeOwnersConfirmationsForm => components}/ScanQRModal/style.js (100%) rename src/{routes/open/components/SafeOwnersConfirmationsForm => components}/ScanQRModal/utils.js (100%) diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/index.jsx b/src/components/ScanQRModal/index.jsx similarity index 100% rename from src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/index.jsx rename to src/components/ScanQRModal/index.jsx diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/style.js b/src/components/ScanQRModal/style.js similarity index 100% rename from src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/style.js rename to src/components/ScanQRModal/style.js diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/utils.js b/src/components/ScanQRModal/utils.js similarity index 100% rename from src/routes/open/components/SafeOwnersConfirmationsForm/ScanQRModal/utils.js rename to src/components/ScanQRModal/utils.js diff --git a/src/routes/open/components/SafeOwnersConfirmationsForm/index.jsx b/src/routes/open/components/SafeOwnersConfirmationsForm/index.jsx index 54b6ac95..d1ac6441 100644 --- a/src/routes/open/components/SafeOwnersConfirmationsForm/index.jsx +++ b/src/routes/open/components/SafeOwnersConfirmationsForm/index.jsx @@ -28,7 +28,7 @@ import { getAccountsFrom } from '~/routes/open/utils/safeDataExtractor' import Hairline from '~/components/layout/Hairline' import trash from '~/assets/icons/trash.svg' import QRIcon from '~/assets/icons/qrcode.svg' -import ScanQRModal from './ScanQRModal' +import ScanQRModal from '~/components/ScanQRModal' import { getAddressValidator } from './validators' import { styles } from './style' diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx index b36d895e..aa58ddd0 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/index.jsx @@ -1,5 +1,5 @@ // @flow -import React from 'react' +import React, { useState } from 'react' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import InputAdornment from '@material-ui/core/InputAdornment' @@ -10,19 +10,19 @@ import GnoForm from '~/components/forms/GnoForm' import AddressInput from '~/components/forms/AddressInput' import Col from '~/components/layout/Col' import Button from '~/components/layout/Button' +import ScanQRModal from '~/components/ScanQRModal' import Block from '~/components/layout/Block' +import Img from '~/components/layout/Img' import Hairline from '~/components/layout/Hairline' import ButtonLink from '~/components/layout/ButtonLink' import Field from '~/components/forms/Field' import TextField from '~/components/forms/TextField' import TextareaField from '~/components/forms/TextareaField' import { - composeValidators, - mustBeFloat, - maxValue, - mustBeEthereumContractAddress, + composeValidators, mustBeFloat, maxValue, mustBeEthereumContractAddress, } from '~/components/forms/validator' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' +import QRIcon from '~/assets/icons/qrcode.svg' import ArrowDown from '../assets/arrow-down.svg' import { styles } from './style' @@ -47,12 +47,21 @@ const SendCustomTx = ({ onSubmit, initialValues, }: Props) => { + const [qrModalOpen, setQrModalOpen] = useState(false) const handleSubmit = (values: Object) => { if (values.data || values.value) { onSubmit(values) } } + const openQrModal = () => { + setQrModalOpen(true) + } + + const closeQrModal = () => { + setQrModalOpen(false) + } + const formMutators = { setMax: (args, state, utils) => { utils.changeValue(state, 'value', () => ethBalance) @@ -78,6 +87,17 @@ const SendCustomTx = ({ {(...args) => { const mutators = args[3] + const handleScan = (value) => { + let scannedAddress = value + + if (scannedAddress.startsWith('ethereum:')) { + scannedAddress = scannedAddress.replace('ethereum:', '') + } + + mutators.setRecipient(scannedAddress) + closeQrModal() + } + return ( <> @@ -96,7 +116,7 @@ const SendCustomTx = ({ - + + + Scan QR { + openQrModal() + }} + /> + @@ -124,10 +156,7 @@ const SendCustomTx = ({ name="value" component={TextField} type="text" - validate={composeValidators( - mustBeFloat, - maxValue(ethBalance), - )} + validate={composeValidators(mustBeFloat, maxValue(ethBalance))} placeholder="Value*" text="Value*" className={classes.addressInput} @@ -164,6 +193,7 @@ const SendCustomTx = ({ Review + {qrModalOpen && } ) }} diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js index 96a1149e..38f2a9e5 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js +++ b/src/routes/safe/components/Balances/SendModal/screens/SendCustomTx/style.js @@ -21,6 +21,9 @@ export const styles = () => ({ height: '35px', width: '35px', }, + qrCodeBtn: { + cursor: 'pointer', + }, formContainer: { padding: `${md} ${lg}`, }, diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx index 0d342252..68d9658b 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx @@ -1,5 +1,5 @@ // @flow -import React from 'react' +import React, { useState } from 'react' import { List } from 'immutable' import { withStyles } from '@material-ui/core/styles' import { OnChange } from 'react-final-form-listeners' @@ -13,6 +13,7 @@ import AddressInput from '~/components/forms/AddressInput' import Col from '~/components/layout/Col' import Button from '~/components/layout/Button' import Block from '~/components/layout/Block' +import Img from '~/components/layout/Img' import Hairline from '~/components/layout/Hairline' import ButtonLink from '~/components/layout/ButtonLink' import Field from '~/components/forms/Field' @@ -23,7 +24,9 @@ import { } from '~/components/forms/validator' import TokenSelectField from '~/routes/safe/components/Balances/SendModal/screens/SendFunds/TokenSelectField' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' +import ScanQRModal from '~/components/ScanQRModal' import ArrowDown from '../assets/arrow-down.svg' +import QRIcon from '~/assets/icons/qrcode.svg' import { styles } from './style' type Props = { @@ -39,6 +42,20 @@ type Props = { initialValues: Object, } +const formMutators = { + setMax: (args, state, utils) => { + const { token } = state.formState.values + + utils.changeValue(state, 'amount', () => token && token.balance) + }, + onTokenChange: (args, state, utils) => { + utils.changeValue(state, 'amount', () => '') + }, + setRecipient: (args, state, utils) => { + utils.changeValue(state, 'recipientAddress', () => args[0]) + }, +} + const SendFunds = ({ classes, onClose, @@ -51,22 +68,18 @@ const SendFunds = ({ initialValues, onSubmit, }: Props) => { + const [qrModalOpen, setQrModalOpen] = useState(false) + const handleSubmit = (values) => { onSubmit(values) } - const formMutators = { - setMax: (args, state, utils) => { - const { token } = state.formState.values + const openQrModal = () => { + setQrModalOpen(true) + } - utils.changeValue(state, 'amount', () => token && token.balance) - }, - onTokenChange: (args, state, utils) => { - utils.changeValue(state, 'amount', () => '') - }, - setRecipient: (args, state, utils) => { - utils.changeValue(state, 'recipientAddress', () => args[0]) - }, + const closeQrModal = () => { + setQrModalOpen(false) } return ( @@ -86,6 +99,18 @@ const SendFunds = ({ const formState = args[2] const mutators = args[3] const { token } = formState.values + + const handleScan = (value) => { + let scannedAddress = value + + if (scannedAddress.startsWith('ethereum:')) { + scannedAddress = scannedAddress.replace('ethereum:', '') + } + + mutators.setRecipient(scannedAddress) + closeQrModal() + } + return ( <> @@ -104,7 +129,7 @@ const SendFunds = ({ - + + + Scan QR { + openQrModal() + }} + /> + @@ -175,6 +212,7 @@ const SendFunds = ({ Review + {qrModalOpen && } ) }} diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js index 31d3c991..85ddeac7 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js +++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/style.js @@ -21,6 +21,9 @@ export const styles = () => ({ height: '35px', width: '35px', }, + qrCodeBtn: { + cursor: 'pointer', + }, formContainer: { padding: `${md} ${lg}`, }, From 4de2e26214555ee21ff8b81308565ca15a59faa7 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 16:54:40 +0400 Subject: [PATCH 04/42] ReviewTx import order fix --- .../components/Balances/SendModal/screens/ReviewTx/index.jsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index 2dd89813..bfa82e58 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -21,9 +21,9 @@ import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchToke import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { getWeb3 } from '~/logic/wallets/getWeb3' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' -import ArrowDown from '../assets/arrow-down.svg' import { secondary } from '~/theme/variables' import { isEther } from '~/logic/tokens/utils/tokenHelpers' +import ArrowDown from '../assets/arrow-down.svg' import { styles } from './style' type Props = { From 19029819e20fa4bc2285a0b0850d1a1ef98c484a Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 17:09:29 +0400 Subject: [PATCH 05/42] Fix sending TX for tokens with decimals other than 24 --- .../components/Balances/SendModal/screens/ReviewTx/index.jsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index bfa82e58..05fa129c 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -1,5 +1,6 @@ // @flow import React from 'react' +import { BigNumber } from 'bignumber.js' import OpenInNew from '@material-ui/icons/OpenInNew' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' @@ -68,6 +69,8 @@ const ReviewTx = ({ if (!isSendingETH) { const StandardToken = await getStandardTokenContract() const tokenInstance = await StandardToken.at(tx.token.address) + const decimals = await tokenInstance.decimals() + txAmount = new BigNumber(tx.amount).div(10 ** decimals).toString() txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, txAmount).encodeABI() // txAmount should be 0 if we send tokens From b458aa44ba86d87e876a96a9b480ad20fcecdcea Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 17:35:24 +0400 Subject: [PATCH 06/42] Fix a fix for sending TX for tokens with decimals other than 24 --- .../Balances/SendModal/screens/ReviewTx/index.jsx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index 05fa129c..88ecd661 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -18,7 +18,7 @@ import { copyToClipboard } from '~/utils/clipboard' import Hairline from '~/components/layout/Hairline' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' -import { getStandardTokenContract } from '~/logic/tokens/store/actions/fetchTokens' +import { getStandardTokenContract, getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { getWeb3 } from '~/logic/wallets/getWeb3' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' @@ -68,9 +68,11 @@ const ReviewTx = ({ if (!isSendingETH) { const StandardToken = await getStandardTokenContract() + const HumanFriendlyToken = await getHumanFriendlyToken() const tokenInstance = await StandardToken.at(tx.token.address) - const decimals = await tokenInstance.decimals() - txAmount = new BigNumber(tx.amount).div(10 ** decimals).toString() + const hfTokenInstance = await HumanFriendlyToken.at(tx.token.address) + const decimals = await hfTokenInstance.decimals() + txAmount = new BigNumber(tx.amount).times(10 ** decimals.toNumber()).toString() txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, txAmount).encodeABI() // txAmount should be 0 if we send tokens From 051b6776511ae29f2c356a404cee81fa4aa0677d Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 4 Oct 2019 18:10:17 +0400 Subject: [PATCH 07/42] Safe view tabs as routes wip --- src/routes/safe/components/Layout.jsx | 108 ++++++++++++++------------ 1 file changed, 59 insertions(+), 49 deletions(-) diff --git a/src/routes/safe/components/Layout.jsx b/src/routes/safe/components/Layout.jsx index d49c838e..f2e69274 100644 --- a/src/routes/safe/components/Layout.jsx +++ b/src/routes/safe/components/Layout.jsx @@ -1,6 +1,7 @@ // @flow import * as React from 'react' import classNames from 'classnames/bind' +import { Switch, Redirect, Route } from 'react-router-dom' import OpenInNew from '@material-ui/icons/OpenInNew' import Tabs from '@material-ui/core/Tabs' import Tab from '@material-ui/core/Tab' @@ -21,9 +22,7 @@ import Receive from './Balances/Receive' import NoSafe from '~/components/NoSafe' import { type SelectorProps } from '~/routes/safe/container/selector' import { getEtherScanLink } from '~/logic/wallets/getWeb3' -import { - secondary, border, -} from '~/theme/variables' +import { secondary, border } from '~/theme/variables' import { copyToClipboard } from '~/utils/clipboard' import { type Actions } from '../container/actions' import Balances from './Balances' @@ -49,7 +48,7 @@ type Props = SelectorProps & onShow: Function, onHide: Function, showSendFunds: Function, - hideSendFunds: Function + hideSendFunds: Function, } const openIconStyle = { @@ -99,6 +98,7 @@ class Layout extends React.Component { showSendFunds, hideSendFunds, } = this.props + console.log(this.props) const { tabIndex } = this.state if (!safe) { @@ -139,7 +139,7 @@ class Layout extends React.Component { disabled={!granted} > - Send + Send @@ -162,46 +162,61 @@ class Layout extends React.Component { - {tabIndex === 0 && ( - + ( + + )} /> - )} - {tabIndex === 1 && ( - ( + + )} /> - )} - {tabIndex === 2 && ( - ( + + )} /> - )} + + { open={showReceive} paperClassName={classes.receiveModal} > - + ) From 2f13ecc589b5caee317a306c2851b9a55da7655c Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Mon, 7 Oct 2019 14:57:34 +0400 Subject: [PATCH 08/42] Integrate material ui tabs with react router --- package.json | 22 +- src/routes/index.js | 6 +- src/routes/safe/components/Layout.jsx | 358 +++++++++--------- .../Transactions/TxsTable/columns.js | 3 +- .../safe/store/actions/fetchTransactions.js | 6 +- src/routes/safe/store/models/transaction.js | 2 + yarn.lock | 314 +++++++++------ 7 files changed, 388 insertions(+), 323 deletions(-) diff --git a/package.json b/package.json index c0bc890d..f1e011b4 100644 --- a/package.json +++ b/package.json @@ -33,10 +33,10 @@ "dependencies": { "@gnosis.pm/safe-contracts": "^1.0.0", "@gnosis.pm/util-contracts": "2.0.4", - "@material-ui/core": "4.4.3", + "@material-ui/core": "4.5.0", "@material-ui/icons": "4.4.3", "@testing-library/jest-dom": "4.1.0", - "@welldone-software/why-did-you-render": "3.3.5", + "@welldone-software/why-did-you-render": "3.3.6", "axios": "0.19.0", "bignumber.js": "9.0.0", "connected-react-router": "6.5.2", @@ -50,8 +50,8 @@ "notistack": "https://github.com/gnosis/notistack.git#v0.9.4", "optimize-css-assets-webpack-plugin": "5.0.3", "qrcode.react": "^0.9.3", - "react": "16.10.1", - "react-dom": "16.10.1", + "react": "16.10.2", + "react-dom": "16.10.2", "react-final-form": "6.3.0", "react-final-form-listeners": "^1.0.2", "react-hot-loader": "4.12.14", @@ -96,8 +96,8 @@ "@storybook/addon-knobs": "5.2.1", "@storybook/addon-links": "5.2.1", "@storybook/react": "5.2.1", - "@testing-library/react": "9.2.0", - "autoprefixer": "9.6.1", + "@testing-library/react": "9.3.0", + "autoprefixer": "9.6.4", "babel-core": "^7.0.0-bridge.0", "babel-eslint": "10.0.3", "babel-jest": "24.9.0", @@ -118,7 +118,7 @@ "ethereumjs-abi": "0.6.8", "extract-text-webpack-plugin": "^4.0.0-beta.0", "file-loader": "4.2.0", - "flow-bin": "0.108.0", + "flow-bin": "0.109.0", "fs-extra": "8.1.0", "html-loader": "^0.5.5", "html-webpack-plugin": "^3.2.0", @@ -135,15 +135,15 @@ "storybook-host": "5.1.0", "storybook-router": "^0.3.4", "style-loader": "1.0.0", - "truffle": "5.0.38", + "truffle": "5.0.39", "truffle-contract": "4.0.31", "truffle-solidity-loader": "0.1.32", "uglifyjs-webpack-plugin": "2.2.0", - "url-loader": "^2.1.0", + "url-loader": "2.2.0", "webpack": "4.41.0", "webpack-bundle-analyzer": "3.5.2", "webpack-cli": "3.3.9", - "webpack-dev-server": "3.8.1", - "webpack-manifest-plugin": "^2.1.2" + "webpack-dev-server": "3.8.2", + "webpack-manifest-plugin": "2.2.0" } } diff --git a/src/routes/index.js b/src/routes/index.js index 79bd4845..488622e0 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -24,7 +24,7 @@ const Opening = React.lazy(() => import('./opening/container')) const Load = React.lazy(() => import('./load/container/Load')) -const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}` +export const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}` type RoutesProps = { defaultSafe?: string, @@ -64,10 +64,10 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => { /> - + - + {/* */} ) } diff --git a/src/routes/safe/components/Layout.jsx b/src/routes/safe/components/Layout.jsx index f2e69274..fde8b8d3 100644 --- a/src/routes/safe/components/Layout.jsx +++ b/src/routes/safe/components/Layout.jsx @@ -1,7 +1,9 @@ // @flow import * as React from 'react' import classNames from 'classnames/bind' -import { Switch, Redirect, Route } from 'react-router-dom' +import { + Switch, Redirect, Route, withRouter, +} from 'react-router-dom' import OpenInNew from '@material-ui/icons/OpenInNew' import Tabs from '@material-ui/core/Tabs' import Tab from '@material-ui/core/Tab' @@ -23,7 +25,6 @@ import NoSafe from '~/components/NoSafe' import { type SelectorProps } from '~/routes/safe/container/selector' import { getEtherScanLink } from '~/logic/wallets/getWeb3' import { secondary, border } from '~/theme/variables' -import { copyToClipboard } from '~/utils/clipboard' import { type Actions } from '../container/actions' import Balances from './Balances' import Transactions from './Transactions' @@ -35,10 +36,6 @@ export const SETTINGS_TAB_BTN_TEST_ID = 'settings-tab-btn' export const TRANSACTIONS_TAB_BTN_TEST_ID = 'transactions-tab-btn' export const SAFE_VIEW_NAME_HEADING_TEST_ID = 'safe-name-heading' -type State = { - tabIndex: number, -} - type Props = SelectorProps & Actions & { classes: Object, @@ -49,6 +46,9 @@ type Props = SelectorProps & onHide: Function, showSendFunds: Function, hideSendFunds: Function, + match: Object, + location: Object, + history: Object, } const openIconStyle = { @@ -56,191 +56,181 @@ const openIconStyle = { color: secondary, } -class Layout extends React.Component { - constructor(props) { - super(props) - this.state = { - tabIndex: 0, - } +const Layout = (props: Props) => { + const { + safe, + provider, + network, + classes, + granted, + tokens, + activeTokens, + createTransaction, + processTransaction, + fetchTransactions, + updateSafe, + transactions, + userAddress, + sendFunds, + showReceive, + onShow, + onHide, + showSendFunds, + hideSendFunds, + match, + location, + } = props + + const handleCallToRouter = (_, value) => { + const { history } = props + + history.push(value) } - handleChange = (event, tabIndex) => { - this.setState({ tabIndex }) + if (!safe) { + return } - copyAddress = () => { - const { safe } = this.props + const { address, ethBalance, name } = safe + const etherScanLink = getEtherScanLink('address', address) - if (safe.address) { - copyToClipboard(safe.address) - } - } - - render() { - const { - safe, - provider, - network, - classes, - granted, - tokens, - activeTokens, - createTransaction, - processTransaction, - fetchTransactions, - updateSafe, - transactions, - userAddress, - sendFunds, - showReceive, - onShow, - onHide, - showSendFunds, - hideSendFunds, - } = this.props - console.log(this.props) - const { tabIndex } = this.state - - if (!safe) { - return - } - - const { address, ethBalance, name } = safe - const etherScanLink = getEtherScanLink('address', address) - - return ( - <> - - - - - - {name} - - {!granted && Read Only} - - - - {address} - - - - - - - - - - - + return ( + <> + + + + + + {name} + + {!granted && Read Only} + + + + {address} + + + + - - - - - - - - - - ( - - )} - /> - ( - - )} - /> - ( - - )} - /> - - - - + + + + + + + + - - - - ) - } + + + + + + + + ( + + )} + /> + ( + + )} + /> + ( + + )} + /> + + + + + + + + ) } -export default withStyles(styles)(Layout) +export default withStyles(styles)(withRouter(Layout)) diff --git a/src/routes/safe/components/Transactions/TxsTable/columns.js b/src/routes/safe/components/Transactions/TxsTable/columns.js index a908e375..ca95438a 100644 --- a/src/routes/safe/components/Transactions/TxsTable/columns.js +++ b/src/routes/safe/components/Transactions/TxsTable/columns.js @@ -1,5 +1,6 @@ // @flow import { format, getTime, parseISO } from 'date-fns' +import { BigNumber } from 'bignumber.js' import { List } from 'immutable' import { type Transaction } from '~/routes/safe/store/models/transaction' import { type SortRow, buildOrderFieldFrom } from '~/components/Table/sorting' @@ -32,7 +33,7 @@ export const getTxAmount = (tx: Transaction) => { let txAmount = 'n/a' if (tx.isTokenTransfer && tx.decodedParams) { - txAmount = `${fromWei(toBN(tx.decodedParams.value), 'ether')} ${tx.symbol}` + txAmount = `${new BigNumber(tx.decodedParams.value).div(10 ** tx.decimals.toNumber()).toString()} ${tx.symbol}` } else if (Number(tx.value) > 0) { txAmount = `${fromWei(toBN(tx.value), 'ether')} ${tx.symbol}` } diff --git a/src/routes/safe/store/actions/fetchTransactions.js b/src/routes/safe/store/actions/fetchTransactions.js index 2dcf4b15..cfe3dad5 100644 --- a/src/routes/safe/store/actions/fetchTransactions.js +++ b/src/routes/safe/store/actions/fetchTransactions.js @@ -70,11 +70,12 @@ export const buildTransactionFrom = async ( } let symbol = 'ETH' + let decimals = 18 let decodedParams if (isSendTokenTx) { const tokenContract = await getHumanFriendlyToken() - const tokenInstance = await tokenContract.at(tx.to) - symbol = await tokenInstance.symbol() + const tokenInstance = await tokenContract.at(tx.to); + [symbol, decimals] = await Promise.all([tokenInstance.symbol(), tokenInstance.decimals()]) const params = web3.eth.abi.decodeParameters(['address', 'uint256'], tx.data.slice(10)) decodedParams = { @@ -93,6 +94,7 @@ export const buildTransactionFrom = async ( nonce: tx.nonce, value: tx.value.toString(), confirmations, + decimals, recipient: tx.to, data: tx.data ? tx.data : EMPTY_DATA, isExecuted: tx.isExecuted, diff --git a/src/routes/safe/store/models/transaction.js b/src/routes/safe/store/models/transaction.js index 27cd179f..c0b598da 100644 --- a/src/routes/safe/store/models/transaction.js +++ b/src/routes/safe/store/models/transaction.js @@ -21,6 +21,7 @@ export type TransactionProps = { customTx: boolean, safeTxHash: string, executionTxHash?: string, + decimals?: number, cancelled?: boolean, status?: TransactionStatus, isTokenTransfer: boolean, @@ -45,6 +46,7 @@ export const makeTransaction: RecordFactory = Record({ cancellationTx: false, customTx: false, status: 'awaiting', + decimals: 18, isTokenTransfer: false, decodedParams: {}, }) diff --git a/yarn.lock b/yarn.lock index 6163fbe6..6d7865b2 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1760,14 +1760,14 @@ "@types/istanbul-reports" "^1.1.1" "@types/yargs" "^13.0.0" -"@material-ui/core@4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.4.3.tgz#65665d2c4e9cb84e018774e1471f6d0417f4535e" - integrity sha512-Lz8sMFeCrtq5/pbhqClWFHpveL0huixjca0tw7uvh9xKKB7VyyYOyTu7RamSZLxb34UCSMPlobR+KK25Nqzkqw== +"@material-ui/core@4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@material-ui/core/-/core-4.5.0.tgz#7e57cc40988c71b6340e3b2569b47dbac1820351" + integrity sha512-UHVAjU+1uDtA+OMBNBHb4RlCZOu514XeYPafNJv+GTdXBDr1SCPK7yqRE6TV1/bulxlDusTgu5Q6BAUgpmO4MA== dependencies: "@babel/runtime" "^7.4.4" - "@material-ui/styles" "^4.4.3" - "@material-ui/system" "^4.4.3" + "@material-ui/styles" "^4.5.0" + "@material-ui/system" "^4.5.0" "@material-ui/types" "^4.1.1" "@material-ui/utils" "^4.4.0" "@types/react-transition-group" "^4.2.0" @@ -1788,10 +1788,10 @@ dependencies: "@babel/runtime" "^7.4.4" -"@material-ui/styles@^4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.4.3.tgz#78239177723660093cc9a277db5759c01c693c2a" - integrity sha512-kNUdHFWsrvWKIEPx8Xy2/qayqsGMrYmCMq+FIiJiYczVZl5hiS8j5+KayonnpVta/O+Dktk+cxWkVcgwtxMrHg== +"@material-ui/styles@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@material-ui/styles/-/styles-4.5.0.tgz#4e591b8d44c7ecce318634bd8ac652499b6c277a" + integrity sha512-O0NSAECHK9f3DZK6wy56PZzp8b/7KSdfpJs8DSC7vnXUAoMPCTtchBKLzMtUsNlijiJFeJjSxNdQfjWXgyur5A== dependencies: "@babel/runtime" "^7.4.4" "@emotion/hash" "^0.7.1" @@ -1801,20 +1801,20 @@ csstype "^2.5.2" deepmerge "^4.0.0" hoist-non-react-statics "^3.2.1" - jss "10.0.0-alpha.25" - jss-plugin-camel-case "10.0.0-alpha.25" - jss-plugin-default-unit "10.0.0-alpha.25" - jss-plugin-global "10.0.0-alpha.25" - jss-plugin-nested "10.0.0-alpha.25" - jss-plugin-props-sort "10.0.0-alpha.25" - jss-plugin-rule-value-function "10.0.0-alpha.25" - jss-plugin-vendor-prefixer "10.0.0-alpha.25" + jss "^10.0.0" + jss-plugin-camel-case "^10.0.0" + jss-plugin-default-unit "^10.0.0" + jss-plugin-global "^10.0.0" + jss-plugin-nested "^10.0.0" + jss-plugin-props-sort "^10.0.0" + jss-plugin-rule-value-function "^10.0.0" + jss-plugin-vendor-prefixer "^10.0.0" prop-types "^15.7.2" -"@material-ui/system@^4.4.3": - version "4.4.3" - resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.4.3.tgz#68ca8cf83614255fcd5b9d3a72ce8ee58a43a5c7" - integrity sha512-Cb05vLXsaCzssXD/iZKa0/qC6YOwbFWnYdnOEdkXZ3Fn2Ytz7rsnMgFejUSQV1luVhUBlEIm8DVz40N25WwW7w== +"@material-ui/system@^4.5.0": + version "4.5.0" + resolved "https://registry.yarnpkg.com/@material-ui/system/-/system-4.5.0.tgz#3235f5d7da8b8af4df425e4f065990c16dee8097" + integrity sha512-vR0PbMTzLnuuVCoYNQ13zyhLa/4s/UA9P9JbNuHBOOkfrHn53ShINiG0v05EgfwizfULLtc7mNvsGAgIyyp/hQ== dependencies: "@babel/runtime" "^7.4.4" deepmerge "^4.0.0" @@ -2531,10 +2531,10 @@ pretty-format "^24.0.0" redent "^3.0.0" -"@testing-library/react@9.2.0": - version "9.2.0" - resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.2.0.tgz#143ad2d96b03c3c334e47aaf33cc2c9b7d007123" - integrity sha512-Hr87KZflfI+vPZjgyzBKQHolQHiXGU5aTGjQSCJdH/yGPbm+IzrvuWPS97GD3RUH3rSS1IXq1e2Sn8Hmyw2ctA== +"@testing-library/react@9.3.0": + version "9.3.0" + resolved "https://registry.yarnpkg.com/@testing-library/react/-/react-9.3.0.tgz#1dabf46d1ea018a1c89acecc0e7b86859b34c0f8" + integrity sha512-FTPCwmLo0tLtP50Au2uGz4/N1BcJTnBx4StDVHZ47zPMEj1/+J2rk/RTj8SLoHRKWCtcmhN4wRmudOXQNP29/w== dependencies: "@babel/runtime" "^7.6.0" "@testing-library/dom" "^6.3.0" @@ -2942,10 +2942,10 @@ "@webassemblyjs/wast-parser" "1.8.5" "@xtuc/long" "4.2.2" -"@welldone-software/why-did-you-render@3.3.5": - version "3.3.5" - resolved "https://registry.yarnpkg.com/@welldone-software/why-did-you-render/-/why-did-you-render-3.3.5.tgz#ba301216ebd7b0283e85995357706223186e6afb" - integrity sha512-hn3U5eU7tVEk3A14tRuYHolbBQv6IRmVd/ukNDeyKmC1IbxBck8j8HnFji4ARvw7Z2kJhUwKtXpRI2+gxVolBA== +"@welldone-software/why-did-you-render@3.3.6": + version "3.3.6" + resolved "https://registry.yarnpkg.com/@welldone-software/why-did-you-render/-/why-did-you-render-3.3.6.tgz#d15cc1b1b15fbe9b29908d02d7c5bad281be1b1e" + integrity sha512-giHh4vPq58CKdvtO1M6cAa0cWWVpFSkXpQPSeVs3O/fm054gVwFKL0ycCcFu/CgnKpip9dfur0i1X7lOH0Gq/Q== dependencies: lodash "^4" @@ -3600,7 +3600,20 @@ atob@^2.1.1: resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9" integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg== -autoprefixer@9.6.1, autoprefixer@^9.4.9: +autoprefixer@9.6.4: + version "9.6.4" + resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.4.tgz#e6453be47af316b2923eaeaed87860f52ad4b7eb" + integrity sha512-Koz2cJU9dKOxG8P1f8uVaBntOv9lP4yz9ffWvWaicv9gHBPhpQB22nGijwd8gqW9CNT+UdkbQOQNLVI8jN1ZfQ== + dependencies: + browserslist "^4.7.0" + caniuse-lite "^1.0.30000998" + chalk "^2.4.2" + normalize-range "^0.1.2" + num2fraction "^1.2.2" + postcss "^7.0.18" + postcss-value-parser "^4.0.2" + +autoprefixer@^9.4.9: version "9.6.1" resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47" integrity sha512-aVo5WxR3VyvyJxcJC3h4FKfwCQvQWb1tSI5VHNibddCVWrcD1NvlxEweg3TSgiPztMnWfjpy2FURKA2kvDE+Tw== @@ -5013,6 +5026,15 @@ browserslist@^4.0.0, browserslist@^4.5.2, browserslist@^4.6.0, browserslist@^4.6 electron-to-chromium "^1.3.191" node-releases "^1.1.25" +browserslist@^4.7.0: + version "4.7.0" + resolved "https://registry.yarnpkg.com/browserslist/-/browserslist-4.7.0.tgz#9ee89225ffc07db03409f2fee524dc8227458a17" + integrity sha512-9rGNDtnj+HaahxiVV38Gn8n8Lr8REKsel68v1sPFfIGEK6uSXTY3h9acgiT1dZVtOOUtifo/Dn8daDQ5dUgVsA== + dependencies: + caniuse-lite "^1.0.30000989" + electron-to-chromium "^1.3.247" + node-releases "^1.1.29" + bs58@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/bs58/-/bs58-2.0.1.tgz#55908d58f1982aba2008fa1bed8f91998a29bf8d" @@ -5314,6 +5336,11 @@ caniuse-lite@^1.0.0, caniuse-lite@^1.0.30000844, caniuse-lite@^1.0.30000955, can resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000985.tgz#0eb40f6c8a8c219155cbe43c4975c0efb4a0f77f" integrity sha512-1ngiwkgqAYPG0JSSUp3PUDGPKKY59EK7NrGGX+VOxaKCNzRbNc7uXMny+c3VJfZxtoK3wSImTvG9T9sXiTw2+w== +caniuse-lite@^1.0.30000989, caniuse-lite@^1.0.30000998: + version "1.0.30000999" + resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30000999.tgz#427253a69ad7bea4aa8d8345687b8eec51ca0e43" + integrity sha512-1CUyKyecPeksKwXZvYw0tEoaMCo/RwBlXmEtN5vVnabvO0KPd9RQLcaAuR9/1F+KDMv6esmOFWlsXuzDk+8rxg== + capture-exit@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/capture-exit/-/capture-exit-2.0.0.tgz#fb953bfaebeb781f62898239dabb426d08a509a4" @@ -7015,6 +7042,11 @@ electron-to-chromium@^1.3.122, electron-to-chromium@^1.3.191, electron-to-chromi resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.199.tgz#f9a62a74cda77854310a2abffde8b75591ea09a1" integrity sha512-gachlDdHSK47s0N2e58GH9HMC6Z4ip0SfmYUa5iEbE50AKaOUXysaJnXMfKj0xB245jWbYcyFSH+th3rqsF8hA== +electron-to-chromium@^1.3.247: + version "1.3.277" + resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.3.277.tgz#38b7b297f9b3f67ea900a965c1b11a555de526ec" + integrity sha512-Czmsrgng89DOgJlIknnw9bn5431QdtnUwGp5YYiPwU1DbZQUxCLF+rc1ZC09VNAdalOPcvH6AE8BaA0H5HjI/w== + element-resize-detector@^1.1.15: version "1.1.15" resolved "https://registry.yarnpkg.com/element-resize-detector/-/element-resize-detector-1.1.15.tgz#48eba1a2eaa26969a4c998d972171128c971d8d2" @@ -8513,10 +8545,10 @@ flatted@^2.0.0: resolved "https://registry.yarnpkg.com/flatted/-/flatted-2.0.1.tgz#69e57caa8f0eacbc281d2e2cb458d46fdb449e08" integrity sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg== -flow-bin@0.108.0: - version "0.108.0" - resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.108.0.tgz#6a42c85fd664d23dd937d925851e8e6ab5d71393" - integrity sha512-hPEyCP1J8rdhNDfCAA5w7bN6HUNBDcHVg/ABU5JVo0gUFMx+uRewpyEH8LlLBGjVQuIpbaPpaqpoaQhAVyaYww== +flow-bin@0.109.0: + version "0.109.0" + resolved "https://registry.yarnpkg.com/flow-bin/-/flow-bin-0.109.0.tgz#dcdcb7402dd85b58200392d8716ccf14e5a8c24c" + integrity sha512-tpcMTpAGIRivYhFV3KJq+zHI2HzcXo8MoGe9pXS4G/UZuey2Faq/e8/gdph2WF0erRlML5hmwfwiq7v9c25c7w== flow-stoplight@^1.0.0: version "1.0.0" @@ -9744,7 +9776,7 @@ http-https@^1.0.0: resolved "https://registry.yarnpkg.com/http-parser-js/-/http-parser-js-0.4.10.tgz#92c9c1374c35085f75db359ec56cc257cbb93fa4" integrity sha1-ksnBN0w1CF912zWexWzCV8u5P6Q= -http-proxy-middleware@^0.19.1: +http-proxy-middleware@0.19.1: version "0.19.1" resolved "https://registry.yarnpkg.com/http-proxy-middleware/-/http-proxy-middleware-0.19.1.tgz#183c7dc4aa1479150306498c210cdaf96080a43a" integrity sha512-yHYTgWMQO8VvwNS22eLLloAkvungsKdKTLO8AJlftYIKNfJr3GK3zK0ZCfzDDGUBttdGc8xFy1mCitvNKQtC3Q== @@ -10070,10 +10102,10 @@ is-absolute-url@^2.0.0: resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-2.1.0.tgz#50530dfb84fcc9aa7dbe7852e83a37b93b9f2aa6" integrity sha1-UFMN+4T8yap9vnhS6Do3uTufKqY= -is-absolute-url@^3.0.2: - version "3.0.2" - resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-3.0.2.tgz#554f2933e7385cc46e94351977ca2081170a206e" - integrity sha512-+5g/wLlcm1AcxSP7014m6GvbPHswDx980vD/3bZaap8aGV9Yfs7Q6y6tfaupgZ5O74Byzc8dGrSCJ+bFXx0KdA== +is-absolute-url@^3.0.3: + version "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" @@ -11290,69 +11322,69 @@ jsqr@^1.2.0: resolved "https://registry.yarnpkg.com/jsqr/-/jsqr-1.2.0.tgz#f93fc65fa7d1ded78b1bcb020fa044352b04261a" integrity sha512-wKcQS9QC2VHGk7aphWCp1RrFyC0CM6fMgC5prZZ2KV/Lk6OKNoCod9IR6bao+yx3KPY0gZFC5dc+h+KFzCI0Wg== -jss-plugin-camel-case@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0-alpha.25.tgz#ea4389de47ccf3b4757f76e62cbb2e8b96b7a2c2" - integrity sha512-J5ZEGDTy9ddqdTUPAF4SJQ25u5kiG1ORP8F+ZPEZAkkiMQJp+/Aol4I7xhTS2aW1Lhg8xNxdhdRfBi5yU7wOvg== +jss-plugin-camel-case@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-camel-case/-/jss-plugin-camel-case-10.0.0.tgz#d601bae2e8e2041cc526add289dcd7062db0a248" + integrity sha512-yALDL00+pPR4FJh+k07A8FeDvfoPPuXU48HLy63enAubcVd3DnS+2rgqPXglHDGixIDVkCSXecl/l5GAMjzIbA== dependencies: "@babel/runtime" "^7.3.1" hyphenate-style-name "^1.0.3" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss-plugin-default-unit@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0-alpha.25.tgz#df5b39bbc0114146101bb3cf8bc7e281e3d0f454" - integrity sha512-auOG459B+yEqkojgaXH02SYO9+xjmAxlmP+WbzhVpXqOFJ2CN/kaxd8P4NJZLdj3BQxHiM7WIyMVh786StE+EA== +jss-plugin-default-unit@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-default-unit/-/jss-plugin-default-unit-10.0.0.tgz#601caf5f576fc0c66986fbe8a9aa37307a3a3ea3" + integrity sha512-sURozIOdCtGg9ap18erQ+ijndAfEGtTaetxfU3H4qwC18Bi+fdvjlY/ahKbuu0ASs7R/+WKCP7UaRZOjUDMcdQ== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss-plugin-global@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.0.0-alpha.25.tgz#2b6a6a14ef6cdb9994dbadf709e480d5c871b5f6" - integrity sha512-cS98Q8X8jwltuaBZd9eYuxMXxkUL+mJGl2Ok3/nmJzH9nLzj6i7kLxSoDtuJNqsRmbP7ogIXVozJUq9lUu2hlQ== +jss-plugin-global@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-global/-/jss-plugin-global-10.0.0.tgz#0fed1b6461e0d57d6e394f877529009bc1cb3cb6" + integrity sha512-80ofWKSQUo62bxLtRoTNe0kFPtHgUbAJeOeR36WEGgWIBEsXLyXOnD5KNnjPqG4heuEkz9eSLccjYST50JnI7Q== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss-plugin-nested@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.0.0-alpha.25.tgz#b8e29d336e1850047914511681d56330e3ea24ac" - integrity sha512-7sk7/6mX1YTgXe+AyeD1zEyKTgIGbbhYtg+wWQcHJlE1flW2JHfcQ5mw84FgHcHQRQ8Dq3l9I3aEY51ev0J1Wg== +jss-plugin-nested@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-nested/-/jss-plugin-nested-10.0.0.tgz#d37ecc013c3b0d0e4acc2b48f6b62da6ae53948b" + integrity sha512-waxxwl/po1hN3azTyixKnr8ReEqUv5WK7WsO+5AWB0bFndML5Yqnt8ARZ90HEg8/P6WlqE/AB2413TkCRZE8bA== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.0-alpha.25" + jss "10.0.0" tiny-warning "^1.0.2" -jss-plugin-props-sort@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0-alpha.25.tgz#dfaa1a6bf9863ae9593b99bf51cd26caea2fe0ec" - integrity sha512-8B/6QLQuUX8cIlZbXdjEm5l0jCX4EgacYMcFJhdKwDKEZYeAghpgQQrCKl0/CYHW7iFge5wim67P+uL6QxMzyw== +jss-plugin-props-sort@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-props-sort/-/jss-plugin-props-sort-10.0.0.tgz#38a13407384c2a4a7c026659488350669b953b18" + integrity sha512-41mf22CImjwNdtOG3r+cdC8+RhwNm616sjHx5YlqTwtSJLyLFinbQC/a4PIFk8xqf1qpFH1kEAIw+yx9HaqZ3g== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss-plugin-rule-value-function@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0-alpha.25.tgz#35350da52334a6031808e197526227434c194277" - integrity sha512-CQQtWO+/OZRGaFRBSGQUgAci9YlVtdoXcWQKBNo70tmpp+kaXKlFNCYaL3jmHbJHMiwKQYG2RYFQNIrwJ9SGmA== +jss-plugin-rule-value-function@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-rule-value-function/-/jss-plugin-rule-value-function-10.0.0.tgz#3ec1b781b7c86080136dbef6c36e91f20244b72e" + integrity sha512-Jw+BZ8JIw1f12V0SERqGlBT1JEPWax3vuZpMym54NAXpPb7R1LYHiCTIlaJUyqvIfEy3kiHMtgI+r2whGgRIxQ== dependencies: "@babel/runtime" "^7.3.1" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss-plugin-vendor-prefixer@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0-alpha.25.tgz#bc0c4b6dcb28d4801775cbad70ad9bc7e0c7707b" - integrity sha512-5FXpB/TiwckbrkoDCmd27YsWCESl1K4hAX/oro2/geEXgnVQvDgQOf2eWCsjYO2K1lYPPXtskMfws/Q3eKmbYg== +jss-plugin-vendor-prefixer@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss-plugin-vendor-prefixer/-/jss-plugin-vendor-prefixer-10.0.0.tgz#400280535b0f483a9c78105afe4eee61b70018eb" + integrity sha512-qslqvL0MUbWuzXJWdUxpj6mdNUX8jr4FFTo3aZnAT65nmzWL7g8oTr9ZxmTXXgdp7ANhS1QWE7036/Q2isFBpw== dependencies: "@babel/runtime" "^7.3.1" css-vendor "^2.0.6" - jss "10.0.0-alpha.25" + jss "10.0.0" -jss@10.0.0-alpha.25: - version "10.0.0-alpha.25" - resolved "https://registry.yarnpkg.com/jss/-/jss-10.0.0-alpha.25.tgz#20a506d8159e3f6bd91e133d54ffd3df0ffd3010" - integrity sha512-zqKnXv181B9vue2yYhmVhc+6ggbbxHF/33rjXfXEjaa22nOvknTI21QDfq3oZ8uCC50kcFp3Z8KU1ghUXdFvIA== +jss@10.0.0, jss@^10.0.0: + version "10.0.0" + resolved "https://registry.yarnpkg.com/jss/-/jss-10.0.0.tgz#998d5026c02accae15708de83bd6ba57bac977d2" + integrity sha512-TPpDFsiBjuERiL+dFDq8QCdiF9oDasPcNqCKLGCo/qED3fNYOQ8PX2lZhknyTiAt3tZrfOFbb0lbQ9lTjPZxsQ== dependencies: "@babel/runtime" "^7.3.1" csstype "^2.6.5" @@ -12645,10 +12677,10 @@ node-fetch@^2.6.0: resolved "https://registry.yarnpkg.com/node-fetch/-/node-fetch-2.6.0.tgz#e633456386d4aa55863f676a7ab0daa8fdecb0fd" integrity sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA== -node-forge@0.8.2: - version "0.8.2" - resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.8.2.tgz#b4bcc59fb12ce77a8825fc6a783dfe3182499c5a" - integrity sha512-mXQ9GBq1N3uDCyV1pdSzgIguwgtVpM7f5/5J4ipz12PKWElmPpVWLDuWl8iXmhysr21+WmX/OJ5UKx82wjomgg== +node-forge@0.9.0: + version "0.9.0" + resolved "https://registry.yarnpkg.com/node-forge/-/node-forge-0.9.0.tgz#d624050edbb44874adca12bb9a52ec63cb782579" + integrity sha512-7ASaDa3pD+lJ3WvXFsxekJQelBKRpne+GOVbLbtHYdd7pFspyeuJHnWfLplGf3SwKGbfs/aYl5V/JCIaHVUKKQ== node-int64@^0.4.0: version "0.4.0" @@ -12732,6 +12764,13 @@ node-releases@^1.1.13, node-releases@^1.1.25: dependencies: semver "^5.3.0" +node-releases@^1.1.29: + version "1.1.34" + resolved "https://registry.yarnpkg.com/node-releases/-/node-releases-1.1.34.tgz#ced4655ee1ba9c3a2c5dcbac385e19434155fd40" + integrity sha512-fNn12JTEfniTuCqo0r9jXgl44+KxRH/huV7zM/KAGOKxDKrHr6EbT7SSs4B+DNxyBE2mks28AD+Jw6PkfY5uwA== + dependencies: + semver "^6.3.0" + nopt@^4.0.1: version "4.0.1" resolved "https://registry.yarnpkg.com/nopt/-/nopt-4.0.1.tgz#d0d4685afd5415193c8c7505602d0d17cd64474d" @@ -14037,6 +14076,11 @@ postcss-value-parser@^4.0.0: resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.0.tgz#99a983d365f7b2ad8d0f9b8c3094926eab4b936d" integrity sha512-ESPktioptiSUchCKgggAkzdmkgzKfmp0EU8jXH+5kbIUB+unr0Y4CY9SRMvibuvYUBjNh1ACLbxqYNpdTQOteQ== +postcss-value-parser@^4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/postcss-value-parser/-/postcss-value-parser-4.0.2.tgz#482282c09a42706d1fc9a069b73f44ec08391dc9" + integrity sha512-LmeoohTpp/K4UiyQCwuGWlONxXamGzCMtFxLq4W1nZVGIQLYvMCJx3yAF9qyyuFpflABI9yVdtJAqbihOsCsJQ== + postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.17, postcss@^7.0.2, postcss@^7.0.5, postcss@^7.0.6: version "7.0.17" resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.17.tgz#4da1bdff5322d4a0acaab4d87f3e782436bad31f" @@ -14046,6 +14090,15 @@ postcss@^7.0.0, postcss@^7.0.1, postcss@^7.0.14, postcss@^7.0.16, postcss@^7.0.1 source-map "^0.6.1" supports-color "^6.1.0" +postcss@^7.0.18: + version "7.0.18" + resolved "https://registry.yarnpkg.com/postcss/-/postcss-7.0.18.tgz#4b9cda95ae6c069c67a4d933029eddd4838ac233" + integrity sha512-/7g1QXXgegpF+9GJj4iN7ChGF40sYuGYJ8WZu8DZWnmhQ/G36hfdk3q9LBJmoK+lZ+yzZ5KYpOoxq7LF1BxE8g== + dependencies: + chalk "^2.4.2" + source-map "^0.6.1" + supports-color "^6.1.0" + pre-commit@^1.2.2: version "1.2.2" resolved "https://registry.yarnpkg.com/pre-commit/-/pre-commit-1.2.2.tgz#dbcee0ee9de7235e57f79c56d7ce94641a69eec6" @@ -14604,15 +14657,15 @@ react-docgen@^4.1.0: node-dir "^0.1.10" recast "^0.17.3" -react-dom@16.10.1: - version "16.10.1" - resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.10.1.tgz#479a6511ba34a429273c213cbc2a9ac4d296dac1" - integrity sha512-SmM4ZW0uug0rn95U8uqr52I7UdNf6wdGLeXDmNLfg3y5q5H9eAbdjF5ubQc3bjDyRrvdAB2IKG7X0GzSpnn5Mg== +react-dom@16.10.2: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react-dom/-/react-dom-16.10.2.tgz#4840bce5409176bc3a1f2bd8cb10b92db452fda6" + integrity sha512-kWGDcH3ItJK4+6Pl9DZB16BXYAZyrYQItU4OMy0jAkv5aNqc+mAKb4TpFtAteI6TJZu+9ZlNhaeNQSVQDHJzkw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" prop-types "^15.6.2" - scheduler "^0.16.1" + scheduler "^0.16.2" react-dom@^16.8.3: version "16.8.6" @@ -14899,10 +14952,10 @@ react-transition-group@^4.3.0: loose-envify "^1.4.0" prop-types "^15.6.2" -react@16.10.1: - version "16.10.1" - resolved "https://registry.yarnpkg.com/react/-/react-16.10.1.tgz#967c1e71a2767dfa699e6ba702a00483e3b0573f" - integrity sha512-2bisHwMhxQ3XQz4LiJJwG3360pY965pTl/MRrZYxIBKVj4fOHoDs5aZAkYXGxDRO1Li+SyjTAilQEbOmtQJHzA== +react@16.10.2: + version "16.10.2" + resolved "https://registry.yarnpkg.com/react/-/react-16.10.2.tgz#a5ede5cdd5c536f745173c8da47bda64797a4cf0" + integrity sha512-MFVIq0DpIhrHFyqLU0S3+4dIcBhhOvBE8bJ/5kHPVOVaGdo0KuiQzpcjCPsf585WvhypqtrMILyoE2th6dT+Lw== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -15804,10 +15857,10 @@ scheduler@^0.13.6: loose-envify "^1.1.0" object-assign "^4.1.1" -scheduler@^0.16.1: - version "0.16.1" - resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.1.tgz#a6fb6ddec12dc2119176e6eb54ecfe69a9eba8df" - integrity sha512-MIuie7SgsqMYOdCXVFZa8SKoNorJZUWHW8dPgto7uEHn1lX3fg2Gu0TzgK8USj76uxV7vB5eRMnZs/cdEHg+cg== +scheduler@^0.16.2: + version "0.16.2" + resolved "https://registry.yarnpkg.com/scheduler/-/scheduler-0.16.2.tgz#f74cd9d33eff6fc554edfb79864868e4819132c1" + integrity sha512-BqYVWqwz6s1wZMhjFvLfVR5WXP7ZY32M/wYPo04CcuPM7XZEbV2TBNW7Z0UkguPTl0dWMA59VbNXxK6q+pHItg== dependencies: loose-envify "^1.1.0" object-assign "^4.1.1" @@ -15845,6 +15898,14 @@ schema-utils@^2.0.1: ajv "^6.1.0" ajv-keywords "^3.1.0" +schema-utils@^2.4.1: + version "2.4.1" + resolved "https://registry.yarnpkg.com/schema-utils/-/schema-utils-2.4.1.tgz#e89ade5d056dc8bcaca377574bb4a9c4e1b8be56" + integrity sha512-RqYLpkPZX5Oc3fw/kHHHyP56fg5Y+XBpIpV8nCg0znIALfq3OH+Ea9Hfeac9BAMwG5IICltiZ0vxFvJQONfA5w== + dependencies: + ajv "^6.10.2" + ajv-keywords "^3.4.1" + scrypt-js@2.0.3: version "2.0.3" resolved "https://registry.yarnpkg.com/scrypt-js/-/scrypt-js-2.0.3.tgz#bb0040be03043da9a012a2cea9fc9f852cfc87d4" @@ -15945,12 +16006,12 @@ select@^1.1.2: resolved "https://registry.yarnpkg.com/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= -selfsigned@^1.10.6: - version "1.10.6" - resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.6.tgz#7b3cd37ed9c2034261a173af1a1aae27d8169b67" - integrity sha512-i3+CeqxL7DpAazgVpAGdKMwHuL63B5nhJMh9NQ7xmChGkA3jNFflq6Jyo1LLJYcr3idWiNOPWHCrm4zMayLG4w== +selfsigned@^1.10.7: + version "1.10.7" + resolved "https://registry.yarnpkg.com/selfsigned/-/selfsigned-1.10.7.tgz#da5819fd049d5574f28e88a9bcc6dbc6e6f3906b" + integrity sha512-8M3wBCzeWIJnQfl43IKwOmC4H/RAp50S8DF60znzjW5GVqTcSe2vWclt7hmYVPkKPlHWOu5EaWOMZ2Y6W8ZXTA== dependencies: - node-forge "0.8.2" + node-forge "0.9.0" semaphore@>=1.0.1, semaphore@^1.0.3, semaphore@^1.1.0: version "1.1.0" @@ -17852,10 +17913,10 @@ truffle-workflow-compile@^2.1.3: truffle-external-compile "^1.0.15" truffle-resolver "^5.0.15" -truffle@5.0.38: - version "5.0.38" - resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.38.tgz#f306ad1bddea7ad87b795783da2ca9f19a8345dc" - integrity sha512-RmbhEFROg+SGyQRM0tWk4NHUrS/uqHDTcc/ZY0k3JFd4cbr2vGD2csdWCfVW2/+SQ2qufXymurt9aRNvkpYQDA== +truffle@5.0.39: + version "5.0.39" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.39.tgz#5710ba8f60a7184d9eb51d632308f2af0a2e8aff" + integrity sha512-2a17t4o6r0rNMpeQXBc51nXigtIaP9/sU8N2zflaazvzYgDgLMZfqh/dir2mTfyybOsrR47NL310p+6+c8u8VA== dependencies: app-module-path "^2.2.0" mocha "5.2.0" @@ -18229,7 +18290,16 @@ urix@^0.1.0: resolved "https://registry.yarnpkg.com/urix/-/urix-0.1.0.tgz#da937f7a62e21fec1fd18d49b35c2935067a6c72" integrity sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= -url-loader@^2.0.1, url-loader@^2.1.0: +url-loader@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.2.0.tgz#af321aece1fd0d683adc8aaeb27829f29c75b46e" + integrity sha512-G8nk3np8ZAnwhHXas1JxJEwJyQdqFXAKJehfgZ/XrC48volFBRtO+FIKtF2u0Ma3bw+4vnDVjHPAQYlF9p2vsw== + dependencies: + loader-utils "^1.2.3" + mime "^2.4.4" + schema-utils "^2.4.1" + +url-loader@^2.0.1: version "2.1.0" resolved "https://registry.yarnpkg.com/url-loader/-/url-loader-2.1.0.tgz#bcc1ecabbd197e913eca23f5e0378e24b4412961" integrity sha512-kVrp/8VfEm5fUt+fl2E0FQyrpmOYgMEkBsv8+UDP1wFhszECq5JyGF33I7cajlVY90zRZ6MyfgKXngLvHYZX8A== @@ -19610,10 +19680,10 @@ webpack-dev-middleware@^3.7.0: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-middleware@^3.7.1: - version "3.7.1" - resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.1.tgz#1167aea02afa034489869b8368fe9fed1aea7d09" - integrity sha512-5MWu9SH1z3hY7oHOV6Kbkz5x7hXbxK56mGHNqHTe6d+ewxOwKUxoUJBs7QIaJb33lPjl9bJZ3X0vCoooUzC36A== +webpack-dev-middleware@^3.7.2: + version "3.7.2" + resolved "https://registry.yarnpkg.com/webpack-dev-middleware/-/webpack-dev-middleware-3.7.2.tgz#0019c3db716e3fa5cecbf64f2ab88a74bab331f3" + integrity sha512-1xC42LxbYoqLNAhV6YzTYacicgMZQTqRd27Sim9wn5hJrX3I5nxYy1SxSd4+gjUFsz1dQFj+yEe6zEVmSkeJjw== dependencies: memory-fs "^0.4.1" mime "^2.4.4" @@ -19621,10 +19691,10 @@ webpack-dev-middleware@^3.7.1: range-parser "^1.2.1" webpack-log "^2.0.0" -webpack-dev-server@3.8.1: - version "3.8.1" - resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.1.tgz#485b64c4aadc23f601e72114b40c1b1fea31d9f1" - integrity sha512-9F5DnfFA9bsrhpUCAfQic/AXBVHvq+3gQS+x6Zj0yc1fVVE0erKh2MV4IV12TBewuTrYeeTIRwCH9qLMvdNvTw== +webpack-dev-server@3.8.2: + version "3.8.2" + resolved "https://registry.yarnpkg.com/webpack-dev-server/-/webpack-dev-server-3.8.2.tgz#3292427bf6510da9a3ac2d500b924a4197667ff9" + integrity sha512-0xxogS7n5jHDQWy0WST0q6Ykp7UGj4YvWh+HVN71JoE7BwPxMZrwgraBvmdEMbDVMBzF0u+mEzn8TQzBm5NYJQ== dependencies: ansi-html "0.0.7" bonjour "^3.5.0" @@ -19635,18 +19705,18 @@ webpack-dev-server@3.8.1: del "^4.1.1" express "^4.17.1" html-entities "^1.2.1" - http-proxy-middleware "^0.19.1" + http-proxy-middleware "0.19.1" import-local "^2.0.0" internal-ip "^4.3.0" ip "^1.1.5" - is-absolute-url "^3.0.2" + is-absolute-url "^3.0.3" killable "^1.0.1" loglevel "^1.6.4" opn "^5.5.0" p-retry "^3.0.1" portfinder "^1.0.24" schema-utils "^1.0.0" - selfsigned "^1.10.6" + selfsigned "^1.10.7" semver "^6.3.0" serve-index "^1.9.1" sockjs "0.3.19" @@ -19655,7 +19725,7 @@ webpack-dev-server@3.8.1: strip-ansi "^3.0.1" supports-color "^6.1.0" url "^0.11.0" - webpack-dev-middleware "^3.7.1" + webpack-dev-middleware "^3.7.2" webpack-log "^2.0.0" ws "^6.2.1" yargs "12.0.5" @@ -19678,10 +19748,10 @@ webpack-log@^2.0.0: ansi-colors "^3.0.0" uuid "^3.3.2" -webpack-manifest-plugin@^2.1.2: - version "2.1.2" - resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.1.2.tgz#990c448b4cfe1cf0b2dfad4a422264aabc4c98eb" - integrity sha512-XWjPY0NXXJ1tGQZgtOMZtEsm8mST23nvO7q5e5H26NH4pv7wfYbHaX9Uwogve+IF6Ilv4j1e3hPr9N3JGZdilA== +webpack-manifest-plugin@2.2.0: + version "2.2.0" + resolved "https://registry.yarnpkg.com/webpack-manifest-plugin/-/webpack-manifest-plugin-2.2.0.tgz#19ca69b435b0baec7e29fbe90fb4015de2de4f16" + integrity sha512-9S6YyKKKh/Oz/eryM1RyLVDVmy3NSPV0JXMRhZ18fJsq+AwGxUY34X54VNwkzYcEmEkDwNxuEOboCZEebJXBAQ== dependencies: fs-extra "^7.0.0" lodash ">=3.5 <5" From 7eed7419a98d1b3eb5ec1c4d1efd125bfb808ac8 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Mon, 7 Oct 2019 15:13:37 +0400 Subject: [PATCH 09/42] Remove not needed variable export --- src/routes/index.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/index.js b/src/routes/index.js index 488622e0..864db357 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -24,7 +24,7 @@ const Opening = React.lazy(() => import('./opening/container')) const Load = React.lazy(() => import('./load/container/Load')) -export const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}` +const SAFE_ADDRESS = `${SAFELIST_ADDRESS}/:${SAFE_PARAM_ADDRESS}` type RoutesProps = { defaultSafe?: string, From 712fc80b1e6ca3b835976dc66ada57812410c36f Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Mon, 7 Oct 2019 16:44:24 +0400 Subject: [PATCH 10/42] Switch to transaction list after creating tx, fix typos and missing type imports for notifications --- src/logic/notifications/notificationBuilder.js | 4 ++-- .../safe/components/Settings/ChangeSafeName/index.jsx | 4 ++-- .../Settings/ManageOwners/EditOwnerModal/index.jsx | 4 ++-- src/routes/safe/store/actions/createTransaction.js | 11 +++++++---- src/routes/safe/store/actions/processTransaction.js | 8 ++++---- 5 files changed, 17 insertions(+), 14 deletions(-) diff --git a/src/logic/notifications/notificationBuilder.js b/src/logic/notifications/notificationBuilder.js index 84b5b908..893d17b6 100644 --- a/src/logic/notifications/notificationBuilder.js +++ b/src/logic/notifications/notificationBuilder.js @@ -5,7 +5,7 @@ import { Close as IconClose } from '@material-ui/icons' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { type Notification, NOTIFICATIONS } from './notificationTypes' -type NotificationsQueue = { +export type NotificationsQueue = { beforeExecution: Notification, pendingExecution: { noMoreConfirmationsNeeded: Notification, @@ -104,7 +104,7 @@ const defaultNotificationsQueue: NotificationsQueue = { afterExecutionError: NOTIFICATIONS.TX_FAILED_MSG, } -export const getNofiticationsFromTxType = (txType: string) => { +export const getNotificationsFromTxType = (txType: string) => { let notificationsQueue: NotificationsQueue switch (txType) { diff --git a/src/routes/safe/components/Settings/ChangeSafeName/index.jsx b/src/routes/safe/components/Settings/ChangeSafeName/index.jsx index 764d7994..ec5a969e 100644 --- a/src/routes/safe/components/Settings/ChangeSafeName/index.jsx +++ b/src/routes/safe/components/Settings/ChangeSafeName/index.jsx @@ -12,7 +12,7 @@ import GnoForm from '~/components/forms/GnoForm' import Row from '~/components/layout/Row' import Paragraph from '~/components/layout/Paragraph' import Button from '~/components/layout/Button' -import { getNofiticationsFromTxType, showSnackbar } from '~/logic/notifications' +import { getNotificationsFromTxType, showSnackbar } from '~/logic/notifications' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { styles } from './style' @@ -36,7 +36,7 @@ const ChangeSafeName = (props: Props) => { const handleSubmit = (values) => { updateSafe({ address: safeAddress, name: values.safeName }) - const notification = getNofiticationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX) + const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.SAFE_NAME_CHANGE_TX) showSnackbar(notification.afterExecution, enqueueSnackbar, closeSnackbar) } diff --git a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx index 867d9db3..0f9bddb0 100644 --- a/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/EditOwnerModal/index.jsx @@ -16,7 +16,7 @@ import TextField from '~/components/forms/TextField' import Paragraph from '~/components/layout/Paragraph' import Identicon from '~/components/Identicon' import { composeValidators, required, minMaxLength } from '~/components/forms/validator' -import { getNofiticationsFromTxType, showSnackbar } from '~/logic/notifications' +import { getNotificationsFromTxType, showSnackbar } from '~/logic/notifications' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { getEtherScanLink } from '~/logic/wallets/getWeb3' import Modal from '~/components/Modal' @@ -57,7 +57,7 @@ const EditOwnerComponent = ({ const handleSubmit = (values) => { editSafeOwner({ safeAddress, ownerAddress, ownerName: values.ownerName }) - const notification = getNofiticationsFromTxType(TX_NOTIFICATION_TYPES.OWNER_NAME_CHANGE_TX) + const notification = getNotificationsFromTxType(TX_NOTIFICATION_TYPES.OWNER_NAME_CHANGE_TX) showSnackbar(notification.afterExecution, enqueueSnackbar, closeSnackbar) onClose() diff --git a/src/routes/safe/store/actions/createTransaction.js b/src/routes/safe/store/actions/createTransaction.js index 4478bd8a..8de8f141 100644 --- a/src/routes/safe/store/actions/createTransaction.js +++ b/src/routes/safe/store/actions/createTransaction.js @@ -1,5 +1,6 @@ // @flow import type { Dispatch as ReduxDispatch, GetState } from 'redux' +import { push } from 'connected-react-router' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { userAccountSelector } from '~/logic/wallets/store/selectors' import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions' @@ -17,11 +18,12 @@ import { import { type Notification, type NotificationsQueue, - getNofiticationsFromTxType, + getNotificationsFromTxType, showSnackbar, } from '~/logic/notifications' import { getErrorMessage } from '~/test/utils/ethereumErrors' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' +import { SAFELIST_ADDRESS } from '~/routes/routes' const createTransaction = ( safeAddress: string, @@ -35,6 +37,8 @@ const createTransaction = ( ) => async (dispatch: ReduxDispatch, getState: GetState) => { const state: GlobalState = getState() + dispatch(push(`${SAFELIST_ADDRESS}/${safeAddress}/transactions`)) + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const from = userAccountSelector(state) const threshold = await safeInstance.getThreshold() @@ -47,7 +51,7 @@ const createTransaction = ( '', )}000000000000000000000000000000000000000000000000000000000000000001` - const notificationsQueue: NotificationsQueue = getNofiticationsFromTxType(notifiedTransaction) + const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction) const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar) let pendingExecutionKey @@ -99,6 +103,7 @@ const createTransaction = ( if (isExecution) { showSnackbar(notificationsQueue.afterExecution, enqueueSnackbar, closeSnackbar) } + dispatch(fetchTransactions(safeAddress)) return receipt.transactionHash }) @@ -114,8 +119,6 @@ const createTransaction = ( console.error(`Error executing the TX: ${errMsg}`) } - dispatch(fetchTransactions(safeAddress)) - return txHash } diff --git a/src/routes/safe/store/actions/processTransaction.js b/src/routes/safe/store/actions/processTransaction.js index d2ea959d..06ad45e4 100644 --- a/src/routes/safe/store/actions/processTransaction.js +++ b/src/routes/safe/store/actions/processTransaction.js @@ -6,6 +6,7 @@ import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions' import { type GlobalState } from '~/store' import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' import { + type NotifiedTransaction, getApprovalTransaction, getExecutionTransaction, CALL, @@ -16,7 +17,7 @@ import { import { type Notification, type NotificationsQueue, - getNofiticationsFromTxType, + getNotificationsFromTxType, showSnackbar, } from '~/logic/notifications' import { getErrorMessage } from '~/test/utils/ethereumErrors' @@ -68,7 +69,7 @@ const processTransaction = ( )}000000000000000000000000000000000000000000000000000000000000000001` } - const notificationsQueue: NotificationsQueue = getNofiticationsFromTxType(notifiedTransaction) + const notificationsQueue: NotificationsQueue = getNotificationsFromTxType(notifiedTransaction) const beforeExecutionKey = showSnackbar(notificationsQueue.beforeExecution, enqueueSnackbar, closeSnackbar) let pendingExecutionKey @@ -124,6 +125,7 @@ const processTransaction = ( shouldExecute ? TX_TYPE_EXECUTION : TX_TYPE_CONFIRMATION, ) showSnackbar(notificationsQueue.afterExecution, enqueueSnackbar, closeSnackbar) + dispatch(fetchTransactions(safeAddress)) return receipt.transactionHash }) @@ -137,8 +139,6 @@ const processTransaction = ( console.error(`Error executing the TX: ${errMsg}`) } - dispatch(fetchTransactions(safeAddress)) - return txHash } From 387ea01d5ee93bc2a0e4c58c8e1058b83307b9bb Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Mon, 7 Oct 2019 18:50:33 +0400 Subject: [PATCH 11/42] Transactions gas costs wip --- src/logic/safe/transactions/gasNew.js | 57 +++++++++++++++++++ src/logic/wallets/getWeb3.js | 2 +- src/routes/index.js | 2 +- .../SendModal/screens/ReviewTx/index.jsx | 49 +++++++++++++--- 4 files changed, 99 insertions(+), 11 deletions(-) create mode 100644 src/logic/safe/transactions/gasNew.js diff --git a/src/logic/safe/transactions/gasNew.js b/src/logic/safe/transactions/gasNew.js new file mode 100644 index 00000000..05d86336 --- /dev/null +++ b/src/logic/safe/transactions/gasNew.js @@ -0,0 +1,57 @@ +// @flow +import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' +import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' +import { type Operation } from '~/logic/safe/transactions' +import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' +import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' +import { CALL } from '.' + +export const estimateApprovalTxGasCosts = async (safeAddress: string, to: string, data: string): Promise => { + try { + const web3 = getWeb3() + const from = await getAccountFrom(web3) + const safeInstance = new web3.eth.Contract(GnosisSafeSol.abi, safeAddress) + const nonce = await safeInstance.methods.nonce().call() + const txHash = await safeInstance.methods + .getTransactionHash(to, 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce) + .call({ + from, + }) + const approvalData = await safeInstance.methods.approveHash(txHash).encodeABI() + const gas = await calculateGasOf(approvalData, from, safeAddress) + const gasPrice = await calculateGasPrice() + + return gas * parseInt(gasPrice, 10) + } catch (err) { + console.error(`Error while estimating approval transaction gas costs: ${err}`) + + return 1000000000000000 + } +} + +export const estimateExecuteTxGasCosts = async ( + safeInstance: any, + to: string, + valueInWei: number | string, + data: string, + operation: Operation, + nonce: string | number, + sender: string, + sigs: string, +): Promise => { + try { + const web3 = getWeb3() + const contract = new web3.eth.Contract(GnosisSafeSol.abi, safeInstance.address) + const executionData = await contract.methods + .execTransaction(to, valueInWei, data, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS) + .encodeABI() + const gas = await calculateGasOf(executionData, sender, safeInstance.address) + const gasPrice = await calculateGasPrice() + + return gas * parseInt(gasPrice, 10) + } catch (err) { + console.error(`Error while estimating transaction execution gas costs: ${err}`) + + return 0.001 + } +} diff --git a/src/logic/wallets/getWeb3.js b/src/logic/wallets/getWeb3.js index 051b3fa0..1ca597ec 100644 --- a/src/logic/wallets/getWeb3.js +++ b/src/logic/wallets/getWeb3.js @@ -64,7 +64,7 @@ const getProviderName: Function = (web3Provider): string => { return name } -const getAccountFrom: Function = async (web3Provider): Promise => { +export const getAccountFrom: Function = async (web3Provider): Promise => { const accounts = await web3Provider.eth.getAccounts() if (process.env.NODE_ENV === 'test' && window.testAccountIndex) { diff --git a/src/routes/index.js b/src/routes/index.js index 864db357..7df2c155 100644 --- a/src/routes/index.js +++ b/src/routes/index.js @@ -67,7 +67,7 @@ const Routes = ({ defaultSafe, location }: RoutesProps) => { - {/* */} + ) } diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index 88ecd661..81dbb126 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -1,5 +1,5 @@ // @flow -import React from 'react' +import React, { useEffect, useState } from 'react' import { BigNumber } from 'bignumber.js' import OpenInNew from '@material-ui/icons/OpenInNew' import { withStyles } from '@material-ui/core/styles' @@ -19,6 +19,7 @@ import Hairline from '~/components/layout/Hairline' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' import { getStandardTokenContract, getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens' +import { estimateApprovalTxGasCosts } from '~/logic/safe/transactions/gasNew' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { getWeb3 } from '~/logic/wallets/getWeb3' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' @@ -59,10 +60,42 @@ const ReviewTx = ({ enqueueSnackbar, closeSnackbar, }: Props) => { + const [gasCosts, setGasCosts] = useState('0.0') + const isSendingETH = isEther(tx.token.symbol) + const txRecipient = isSendingETH ? tx.recipientAddress : tx.token.address + + useEffect(() => { + let isCurrent = true + const estimateGas = async () => { + const web3 = getWeb3() + const { fromWei, toBN } = web3.utils + let txData = EMPTY_DATA + + if (!isSendingETH) { + const StandardToken = await getStandardTokenContract() + const tokenInstance = await StandardToken.at(tx.token.address) + + txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, 0).encodeABI() + } + + const estimatedGasCosts = await estimateApprovalTxGasCosts(safeAddress, txRecipient, txData) + console.log({ estimatedGasCosts }) + const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') + const roundedGasCosts = parseFloat(gasCostsAsEth).toFixed(3) + if (isCurrent) { + setGasCosts(roundedGasCosts) + } + } + + estimateGas() + + return () => { + isCurrent = false + } + }, []) + const submitTx = async () => { const web3 = getWeb3() - const isSendingETH = isEther(tx.token.symbol) - const txRecipient = isSendingETH ? tx.recipientAddress : tx.token.address let txData = EMPTY_DATA let txAmount = web3.utils.toWei(tx.amount, 'ether') @@ -106,12 +139,7 @@ const ReviewTx = ({ - + Arrow Down @@ -152,6 +180,9 @@ const ReviewTx = ({ + + {`Gas costs: ${gasCosts}`} + - From 85151687e8e4ae7ced8d4bc3537b838db6d17298 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Wed, 9 Oct 2019 17:47:19 +0400 Subject: [PATCH 20/42] Add etherscan/copy buttons to replace owner flow --- .../screens/OwnerForm/index.jsx | 22 +- .../screens/OwnerForm/style.js | 3 + .../screens/Review/index.jsx | 292 ++++++++---------- .../ReplaceOwnerModal/screens/Review/style.js | 3 + 4 files changed, 146 insertions(+), 174 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.jsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.jsx index e573fba9..13bb9c83 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/index.jsx @@ -5,7 +5,8 @@ import { List } from 'immutable' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' -import OpenInNew from '@material-ui/icons/OpenInNew' +import EtherscanBtn from '~/components/EtherscanBtn' +import CopyBtn from '~/components/CopyBtn' import Paragraph from '~/components/layout/Paragraph' import Row from '~/components/layout/Row' import GnoForm from '~/components/forms/GnoForm' @@ -17,24 +18,16 @@ import Hairline from '~/components/layout/Hairline' import Field from '~/components/forms/Field' import TextField from '~/components/forms/TextField' import Identicon from '~/components/Identicon' -import Link from '~/components/layout/Link' -import { getEtherScanLink } from '~/logic/wallets/getWeb3' import { type Owner } from '~/routes/safe/store/models/owner' import { composeValidators, required, minMaxLength, uniqueAddress, } from '~/components/forms/validator' import { styles } from './style' -import { secondary } from '~/theme/variables' export const REPLACE_OWNER_NAME_INPUT_TEST_ID = 'replace-owner-name-input' export const REPLACE_OWNER_ADDRESS_INPUT_TEST_ID = 'replace-owner-address-testid' export const REPLACE_OWNER_NEXT_BTN_TEST_ID = 'replace-owner-next-btn' -const openIconStyle = { - height: '16px', - color: secondary, -} - const formMutators = { setOwnerAddress: (args, state, utils) => { utils.changeValue(state, 'ownerAddress', () => args[0]) @@ -96,16 +89,11 @@ const OwnerForm = ({ {ownerName} - + {ownerAddress} - - - + + diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/style.js b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/style.js index 9a0e2ce3..ada4bbaf 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/style.js +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/OwnerForm/style.js @@ -16,6 +16,9 @@ export const styles = () => ({ marginLeft: '20px', lineHeight: 'normal', }, + address: { + marginRight: sm, + }, manage: { fontSize: '24px', }, diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx index 07ab5583..f15e5183 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx @@ -3,11 +3,11 @@ import React from 'react' import { List } from 'immutable' import classNames from 'classnames' import { withStyles } from '@material-ui/core/styles' -import OpenInNew from '@material-ui/icons/OpenInNew' import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' +import EtherscanBtn from '~/components/EtherscanBtn' +import CopyBtn from '~/components/CopyBtn' import Identicon from '~/components/Identicon' -import Link from '~/components/layout/Link' import Paragraph from '~/components/layout/Paragraph' import Row from '~/components/layout/Row' import Col from '~/components/layout/Col' @@ -15,17 +15,10 @@ import Button from '~/components/layout/Button' import Block from '~/components/layout/Block' import Hairline from '~/components/layout/Hairline' import type { Owner } from '~/routes/safe/store/models/owner' -import { getEtherScanLink } from '~/logic/wallets/getWeb3' -import { secondary } from '~/theme/variables' import { styles } from './style' export const REPLACE_OWNER_SUBMIT_BTN_TEST_ID = 'replace-owner-submit-btn' -const openIconStyle = { - height: '16px', - color: secondary, -} - type Props = { onClose: () => void, classes: Object, @@ -50,165 +43,150 @@ const ReviewRemoveOwner = ({ onClickBack, threshold, onSubmit, -}: Props) => { - const handleSubmit = () => { - onSubmit() - } - - return ( - <> - - +}: Props) => ( + <> + + Replace owner - - 2 of 2 - - - - - - - - - - - - Details - - - - - Safe name - - - {safeName} - - - - - Any transaction requires the confirmation of: - - - {`${threshold} out of ${owners.size} owner(s)`} - - - - - - + + 2 of 2 + + + + + + + + + + - {owners.size} - {' '} - Safe owner(s) + Details - - - {owners.map( - (owner) => owner.address !== ownerAddress && ( - - - - - - - - - {owner.name} + + + + Safe name + + + {safeName} + + + + + Any transaction requires the confirmation of: + + + {`${threshold} out of ${owners.size} owner(s)`} + + + + + + + + {`${owners.size} Safe owner(s)`} + + + + {owners.map( + (owner) => owner.address !== ownerAddress && ( + + + + + + + + + {owner.name} + + + + {owner.address} - - - {owner.address} - - - - - + + - - - - - ), - )} - - + + + + + + ), + )} + + REMOVING OWNER ↓ - - - - - - - - - - - {ownerName} + + + + + + + + + + + {ownerName} + + + + {ownerAddress} - - - {ownerAddress} - - - - - + + - - - - + + + + + ADDING NEW OWNER ↓ - - - - - - - - - - - {values.ownerName} + + + + + + + + + + + {values.ownerName} + + + + {values.ownerAddress} - - - {values.ownerAddress} - - - - - + + - - - - - - - - - - + + + + + - - ) -} + + + + + + + +) export default withStyles(styles)(ReviewRemoveOwner) diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/style.js b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/style.js index 6c3ccc01..20d796e5 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/style.js +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/style.js @@ -22,6 +22,9 @@ export const styles = () => ({ manage: { fontSize: '24px', }, + address: { + marginRight: sm, + }, closeIcon: { height: '35px', width: '35px', From 845d0b93bc5416835a8b64a1e788c475fbcc5942 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Wed, 9 Oct 2019 17:55:49 +0400 Subject: [PATCH 21/42] Add etherscan/copy buttons to remove owner flow --- .../screens/CheckOwner/index.jsx | 18 +++------ .../screens/CheckOwner/style.js | 3 ++ .../RemoveOwnerModal/screens/Review/index.jsx | 37 +++++-------------- .../RemoveOwnerModal/screens/Review/style.js | 3 ++ 4 files changed, 20 insertions(+), 41 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx index 58096ae1..93e6871b 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/index.jsx @@ -4,26 +4,19 @@ import classNames from 'classnames/bind' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' -import OpenInNew from '@material-ui/icons/OpenInNew' import Paragraph from '~/components/layout/Paragraph' import Row from '~/components/layout/Row' import Col from '~/components/layout/Col' import Button from '~/components/layout/Button' import Block from '~/components/layout/Block' import Hairline from '~/components/layout/Hairline' -import Link from '~/components/layout/Link' +import EtherscanBtn from '~/components/EtherscanBtn' +import CopyBtn from '~/components/CopyBtn' import Identicon from '~/components/Identicon' -import { getEtherScanLink } from '~/logic/wallets/getWeb3' import { styles } from './style' -import { secondary } from '~/theme/variables' export const REMOVE_OWNER_MODAL_NEXT_BTN_TEST_ID = 'remove-owner-next-btn' -const openIconStyle = { - height: '16px', - color: secondary, -} - type Props = { onClose: () => void, classes: Object, @@ -65,12 +58,11 @@ const CheckOwner = ({ {ownerName} - + {ownerAddress} - - - + + diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/style.js b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/style.js index 2086f27f..eb685f20 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/style.js +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/CheckOwner/style.js @@ -19,6 +19,9 @@ export const styles = () => ({ manage: { fontSize: '24px', }, + address: { + marginRight: sm, + }, closeIcon: { height: '35px', width: '35px', diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx index 360704ff..7f6f0275 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.jsx @@ -3,11 +3,11 @@ import React from 'react' import { List } from 'immutable' import classNames from 'classnames' import { withStyles } from '@material-ui/core/styles' -import OpenInNew from '@material-ui/icons/OpenInNew' import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' +import EtherscanBtn from '~/components/EtherscanBtn' +import CopyBtn from '~/components/CopyBtn' import Identicon from '~/components/Identicon' -import Link from '~/components/layout/Link' import Paragraph from '~/components/layout/Paragraph' import Row from '~/components/layout/Row' import Col from '~/components/layout/Col' @@ -15,17 +15,10 @@ import Button from '~/components/layout/Button' import Block from '~/components/layout/Block' import Hairline from '~/components/layout/Hairline' import type { Owner } from '~/routes/safe/store/models/owner' -import { getEtherScanLink } from '~/logic/wallets/getWeb3' -import { secondary } from '~/theme/variables' import { styles } from './style' export const REMOVE_OWNER_REVIEW_BTN_TEST_ID = 'remove-owner-review-btn' -const openIconStyle = { - height: '16px', - color: secondary, -} - type Props = { onClose: () => void, classes: Object, @@ -95,9 +88,7 @@ const ReviewRemoveOwner = ({ - {owners.size - 1} - {' '} - Safe owner(s) + {`${owners.size - 1} Safe owner(s)`} @@ -114,16 +105,11 @@ const ReviewRemoveOwner = ({ {owner.name} - + {owner.address} - - - + + @@ -148,16 +134,11 @@ const ReviewRemoveOwner = ({ {ownerName} - + {ownerAddress} - - - + + diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/style.js b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/style.js index f2ab9539..4dbada2c 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/style.js +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/style.js @@ -49,6 +49,9 @@ export const styles = () => ({ ownersTitle: { padding: lg, }, + address: { + marginRight: sm, + }, owner: { padding: sm, alignItems: 'center', From ea04ae976e188198f49544590feb6c8a339b2d55 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Thu, 10 Oct 2019 13:14:16 +0400 Subject: [PATCH 22/42] Add gas costs estimation to change owners for multiple owners safe --- package.json | 2 +- .../ChangeThreshold/index.jsx | 43 ++++++++++++++++--- .../Settings/ThresholdSettings/index.jsx | 1 + yarn.lock | 8 ++-- 4 files changed, 44 insertions(+), 10 deletions(-) diff --git a/package.json b/package.json index d26e68e2..630aba60 100644 --- a/package.json +++ b/package.json @@ -35,7 +35,7 @@ "@gnosis.pm/util-contracts": "2.0.4", "@material-ui/core": "4.5.0", "@material-ui/icons": "4.4.3", - "@testing-library/jest-dom": "4.1.1", + "@testing-library/jest-dom": "4.1.2", "@welldone-software/why-did-you-render": "3.3.7", "axios": "0.19.0", "bignumber.js": "9.0.0", diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx index c7a015f4..55554423 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx @@ -1,5 +1,5 @@ // @flow -import React from 'react' +import React, { useState, useEffect } from 'react' import { List } from 'immutable' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' @@ -18,11 +18,16 @@ import Block from '~/components/layout/Block' import Row from '~/components/layout/Row' import Col from '~/components/layout/Col' import type { Owner } from '~/routes/safe/store/models/owner' +import { getWeb3 } from '~/logic/wallets/getWeb3' +import { formatAmount } from '~/logic/tokens/utils/formatAmount' +import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' +import { estimateApprovalTxGasCosts } from '~/logic/safe/transactions/gasNew' import { styles } from './style' type Props = { onClose: () => void, classes: Object, + safeAddress: string, threshold: number, owners: List, onChangeThreshold: Function, @@ -31,8 +36,33 @@ type Props = { const THRESHOLD_FIELD_NAME = 'threshold' const ChangeThreshold = ({ - onClose, owners, threshold, classes, onChangeThreshold, + onClose, owners, threshold, classes, onChangeThreshold, safeAddress, }: Props) => { + const [gasCosts, setGasCosts] = useState('< 0.001') + + useEffect(() => { + let isCurrent = true + const estimateGasCosts = async () => { + const web3 = getWeb3() + const { fromWei, toBN } = web3.utils + + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) + const txData = safeInstance.contract.methods.changeThreshold('1').encodeABI() + const estimatedGasCosts = await estimateApprovalTxGasCosts(safeAddress, safeAddress, txData) + const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') + const formattedGasCosts = formatAmount(gasCostsAsEth) + if (isCurrent) { + setGasCosts(formattedGasCosts) + } + } + + estimateGasCosts() + + return () => { + isCurrent = false + } + }, []) + const handleSubmit = async (values) => { const newThreshold = values[THRESHOLD_FIELD_NAME] @@ -62,9 +92,7 @@ const ChangeThreshold = ({ - - Any transaction requires the confirmation of: - + Any transaction requires the confirmation of: @@ -96,6 +124,11 @@ const ChangeThreshold = ({ + + + {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCosts} (fee price) ETH in this wallet to fund this confirmation.`} + + diff --git a/src/routes/safe/components/Settings/ThresholdSettings/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/index.jsx index 8afaf2a8..c674d32f 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/index.jsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/index.jsx @@ -98,6 +98,7 @@ const ThresholdSettings = ({ onClose={toggleModal} owners={owners} threshold={threshold} + safeAddress={safeAddress} onChangeThreshold={onChangeThreshold} /> diff --git a/yarn.lock b/yarn.lock index 9e5143d3..39b1958a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2566,10 +2566,10 @@ pretty-format "^24.8.0" wait-for-expect "^1.3.0" -"@testing-library/jest-dom@4.1.1": - version "4.1.1" - resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.1.1.tgz#fe063a97784c8e58fc0498869e689151faeceda3" - integrity sha512-4tMKJ6loIPPoERJoeqSWd0CQDsU+RPdXB1V4dgHgUpJVrObjb+TsWTG2VL32tVeUMZoXqk7cUh14YQXceFzfxg== +"@testing-library/jest-dom@4.1.2": + version "4.1.2" + resolved "https://registry.yarnpkg.com/@testing-library/jest-dom/-/jest-dom-4.1.2.tgz#e523047191379abd67cf0896dfd93cabc7e33eab" + integrity sha512-fNf2rCfu0dBD4DmpzqR2ibsaFlFojrWI/EuU8LLqv73CzFIMvT2RMq88p5JVRe4DfeNj0mu0MQ5FTG4mQ0qFaA== dependencies: "@babel/runtime" "^7.5.1" chalk "^2.4.1" From ee483a98e40dbd82c08711449c55000bedf7fffc Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Thu, 10 Oct 2019 16:47:21 +0400 Subject: [PATCH 23/42] refactor tx gas eestimation method to one functions which supports both execution and approval --- src/logic/safe/transactions/gasNew.js | 64 +++++++++---------- .../screens/ReviewCustomTx/index.jsx | 4 +- .../SendModal/screens/ReviewTx/index.jsx | 4 +- .../ChangeThreshold/index.jsx | 4 +- 4 files changed, 36 insertions(+), 40 deletions(-) diff --git a/src/logic/safe/transactions/gasNew.js b/src/logic/safe/transactions/gasNew.js index 05d86336..df804488 100644 --- a/src/logic/safe/transactions/gasNew.js +++ b/src/logic/safe/transactions/gasNew.js @@ -1,57 +1,53 @@ // @flow import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' -import { type Operation } from '~/logic/safe/transactions' import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' +import { type Transaction } from '~/routes/safe/store/models/transaction' import { CALL } from '.' -export const estimateApprovalTxGasCosts = async (safeAddress: string, to: string, data: string): Promise => { +export const estimateTxGasCosts = async ( + safeAddress: string, + to: string, + data: string, + tx?: Transaction, +): Promise => { try { const web3 = getWeb3() const from = await getAccountFrom(web3) const safeInstance = new web3.eth.Contract(GnosisSafeSol.abi, safeAddress) const nonce = await safeInstance.methods.nonce().call() - const txHash = await safeInstance.methods - .getTransactionHash(to, 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce) - .call({ - from, - }) - const approvalData = await safeInstance.methods.approveHash(txHash).encodeABI() - const gas = await calculateGasOf(approvalData, from, safeAddress) - const gasPrice = await calculateGasPrice() + const threshold = await safeInstance.methods.getThreshold().call() - return gas * parseInt(gasPrice, 10) - } catch (err) { - console.error(`Error while estimating approval transaction gas costs: ${err}`) + const isExecution = (tx && tx.confirmations.size) || threshold === '1' - return 1000000000000000 - } -} + let txData + if (isExecution) { + // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures + const signatures = tx + || `0x000000000000000000000000${from.replace( + '0x', + '', + )}000000000000000000000000000000000000000000000000000000000000000001` + txData = await safeInstance.methods + .execTransaction(to, 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, signatures) + .encodeABI() + } else { + const txHash = await safeInstance.methods + .getTransactionHash(to, 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce) + .call({ + from, + }) + txData = await safeInstance.methods.approveHash(txHash).encodeABI() + } -export const estimateExecuteTxGasCosts = async ( - safeInstance: any, - to: string, - valueInWei: number | string, - data: string, - operation: Operation, - nonce: string | number, - sender: string, - sigs: string, -): Promise => { - try { - const web3 = getWeb3() - const contract = new web3.eth.Contract(GnosisSafeSol.abi, safeInstance.address) - const executionData = await contract.methods - .execTransaction(to, valueInWei, data, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS) - .encodeABI() - const gas = await calculateGasOf(executionData, sender, safeInstance.address) + const gas = await calculateGasOf(txData, from, safeAddress) const gasPrice = await calculateGasPrice() return gas * parseInt(gasPrice, 10) } catch (err) { console.error(`Error while estimating transaction execution gas costs: ${err}`) - return 0.001 + return 10000 } } diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx index 6c76da31..111e906e 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewCustomTx/index.jsx @@ -16,7 +16,7 @@ import EtherscanBtn from '~/components/EtherscanBtn' import CopyBtn from '~/components/CopyBtn' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' -import { estimateApprovalTxGasCosts } from '~/logic/safe/transactions/gasNew' +import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' import { getWeb3 } from '~/logic/wallets/getWeb3' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { getEthAsToken } from '~/logic/tokens/utils/tokenHelpers' @@ -57,7 +57,7 @@ const ReviewCustomTx = ({ const web3 = getWeb3() const { fromWei, toBN } = web3.utils - const estimatedGasCosts = await estimateApprovalTxGasCosts(safeAddress, tx.recipientAddress, tx.data.trim()) + const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.recipientAddress, tx.data.trim()) const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') const formattedGasCosts = formatAmount(gasCostsAsEth) if (isCurrent) { diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index 62452dee..4019fc8c 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -18,7 +18,7 @@ import Hairline from '~/components/layout/Hairline' import SafeInfo from '~/routes/safe/components/Balances/SendModal/SafeInfo' import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' import { getStandardTokenContract, getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens' -import { estimateApprovalTxGasCosts } from '~/logic/safe/transactions/gasNew' +import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' import { formatAmount } from '~/logic/tokens/utils/formatAmount' import { getWeb3 } from '~/logic/wallets/getWeb3' @@ -70,7 +70,7 @@ const ReviewTx = ({ txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, 0).encodeABI() } - const estimatedGasCosts = await estimateApprovalTxGasCosts(safeAddress, txRecipient, txData) + const estimatedGasCosts = await estimateTxGasCosts(safeAddress, txRecipient, txData) const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') const formattedGasCosts = formatAmount(gasCostsAsEth) if (isCurrent) { diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx index 55554423..37ab5e8f 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx @@ -21,7 +21,7 @@ import type { Owner } from '~/routes/safe/store/models/owner' import { getWeb3 } from '~/logic/wallets/getWeb3' import { formatAmount } from '~/logic/tokens/utils/formatAmount' import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' -import { estimateApprovalTxGasCosts } from '~/logic/safe/transactions/gasNew' +import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' import { styles } from './style' type Props = { @@ -48,7 +48,7 @@ const ChangeThreshold = ({ const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const txData = safeInstance.contract.methods.changeThreshold('1').encodeABI() - const estimatedGasCosts = await estimateApprovalTxGasCosts(safeAddress, safeAddress, txData) + const estimatedGasCosts = await estimateTxGasCosts(safeAddress, safeAddress, txData) const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') const formattedGasCosts = formatAmount(gasCostsAsEth) if (isCurrent) { From de16a0cdec7c5890588ecfefe33c36dd5a0afdc4 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Thu, 10 Oct 2019 18:25:33 +0400 Subject: [PATCH 24/42] Gas estimations for approving TX wip, fix notifications reducer which led to infinite loop during HMR --- src/components/Notifier/index.js | 12 +++++-- .../store/reducer/notifications.js | 4 +-- .../notifications/store/selectors/index.js | 2 +- src/logic/safe/transactions/gasNew.js | 9 ++--- .../ExpandedTx/ApproveTxModal/index.jsx | 33 ++++++++++++++++++- src/routes/safe/store/models/safe.js | 4 +-- src/store/index.js | 2 +- 7 files changed, 51 insertions(+), 15 deletions(-) diff --git a/src/components/Notifier/index.js b/src/components/Notifier/index.js index c4a9fd10..009afe0a 100644 --- a/src/components/Notifier/index.js +++ b/src/components/Notifier/index.js @@ -1,11 +1,17 @@ // @flow -import React, { Component } from 'react' +import { Component } from 'react' +import { List } from 'immutable' import { connect } from 'react-redux' import { withSnackbar } from 'notistack' -import actions from './actions' +import { type Notification } from '~/logic/notifications/store/models/notification' +import actions, {type Actions } from './actions' import selector from './selector' -class Notifier extends Component { +type Props = Actions & { + notifications: List, +} + +class Notifier extends Component { displayed = [] shouldComponentUpdate({ notifications: newSnacks = [] }) { diff --git a/src/logic/notifications/store/reducer/notifications.js b/src/logic/notifications/store/reducer/notifications.js index c4ae1c2b..d16e0c0c 100644 --- a/src/logic/notifications/store/reducer/notifications.js +++ b/src/logic/notifications/store/reducer/notifications.js @@ -29,7 +29,5 @@ export default handleActions( return state.delete(key) }, }, - Map({ - notifications: Map(), - }), + Map(), ) diff --git a/src/logic/notifications/store/selectors/index.js b/src/logic/notifications/store/selectors/index.js index 2ae6eb96..bf6371fa 100644 --- a/src/logic/notifications/store/selectors/index.js +++ b/src/logic/notifications/store/selectors/index.js @@ -5,7 +5,7 @@ import { type GlobalState } from '~/store' import { NOTIFICATIONS_REDUCER_ID } from '~/logic/notifications/store/reducer/notifications' import { type Notification } from '~/logic/notifications/store/models/notification' -export const notificationsMapSelector = ( +const notificationsMapSelector = ( state: GlobalState, ): Map => state[NOTIFICATIONS_REDUCER_ID] diff --git a/src/logic/safe/transactions/gasNew.js b/src/logic/safe/transactions/gasNew.js index df804488..da8db6a5 100644 --- a/src/logic/safe/transactions/gasNew.js +++ b/src/logic/safe/transactions/gasNew.js @@ -1,16 +1,17 @@ // @flow import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' +import { List } from 'immutable' +import { type Confirmation } from '~/routes/safe/store/models/confirmation' import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' -import { type Transaction } from '~/routes/safe/store/models/transaction' import { CALL } from '.' export const estimateTxGasCosts = async ( safeAddress: string, to: string, data: string, - tx?: Transaction, + confirmations?: List, ): Promise => { try { const web3 = getWeb3() @@ -19,12 +20,12 @@ export const estimateTxGasCosts = async ( const nonce = await safeInstance.methods.nonce().call() const threshold = await safeInstance.methods.getThreshold().call() - const isExecution = (tx && tx.confirmations.size) || threshold === '1' + const isExecution = (confirmations && confirmations.size) || threshold === '1' let txData if (isExecution) { // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures - const signatures = tx + const signatures = confirmations || `0x000000000000000000000000${from.replace( '0x', '', diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx index b4e12d7d..d470fd98 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx @@ -1,5 +1,5 @@ // @flow -import React, { useState } from 'react' +import React, { useState, useEffect } from 'react' import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' import { withStyles } from '@material-ui/core/styles' @@ -13,6 +13,9 @@ import Row from '~/components/layout/Row' import Bold from '~/components/layout/Bold' import Block from '~/components/layout/Block' import Paragraph from '~/components/layout/Paragraph' +import { getWeb3 } from '~/logic/wallets/getWeb3' +import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' +import { formatAmount } from '~/logic/tokens/utils/formatAmount' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' import { type Transaction } from '~/routes/safe/store/models/transaction' import { styles } from './style' @@ -62,9 +65,32 @@ const ApproveTxModal = ({ closeSnackbar, }: Props) => { const [approveAndExecute, setApproveAndExecute] = useState(true) + const [gasCosts, setGasCosts] = useState('< 0.001') const { title, description } = getModalTitleAndDescription(thresholdReached) const oneConfirmationLeft = tx.confirmations.size + 1 === threshold + useEffect(() => { + let isCurrent = true + + const estimateGas = async () => { + const web3 = getWeb3() + const { fromWei, toBN } = web3.utils + + const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.recipient, tx.data, tx.confirmations) + const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') + const formattedGasCosts = formatAmount(gasCostsAsEth) + if (isCurrent) { + setGasCosts(formattedGasCosts) + } + } + + estimateGas() + + return () => { + isCurrent = false + } + }, [approveAndExecute]) + const handleExecuteCheckbox = () => setApproveAndExecute((prevApproveAndExecute) => !prevApproveAndExecute) const approveTx = () => { @@ -112,6 +138,11 @@ const ApproveTxModal = ({ )} + + + {`You're about to ${approveAndExecute ? 'execute' : 'approve'} a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCosts} (fee price) ETH in this wallet to fund this confirmation.`} + + From 75d37a5f3961e65a63a6029ea1cf5960b1beb51d Mon Sep 17 00:00:00 2001 From: mmv Date: Tue, 15 Oct 2019 15:17:19 +0400 Subject: [PATCH 30/42] Fix 'Send max' functionality when sending funds after send funds refactor --- .../components/Balances/SendModal/index.jsx | 1 + .../SendModal/screens/ReviewTx/index.jsx | 19 +++++--- .../SendModal/screens/SendFunds/index.jsx | 6 +-- .../screens/Review/index.jsx | 45 +++++++++---------- 4 files changed, 37 insertions(+), 34 deletions(-) diff --git a/src/routes/safe/components/Balances/SendModal/index.jsx b/src/routes/safe/components/Balances/SendModal/index.jsx index 20b793a8..9526da23 100644 --- a/src/routes/safe/components/Balances/SendModal/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/index.jsx @@ -128,6 +128,7 @@ const Send = ({ safeName={safeName} ethBalance={ethBalance} createTransaction={createTransaction} + tokens={tokens} /> )} {activeScreen === 'sendCustomTx' && ( diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx index 6328c2cd..d0449fb0 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.jsx @@ -1,5 +1,6 @@ // @flow import React, { useEffect, useState } from 'react' +import { List } from 'immutable' import { BigNumber } from 'bignumber.js' import { withStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' @@ -20,10 +21,11 @@ import { setImageToPlaceholder } from '~/routes/safe/components/Balances/utils' import { getHumanFriendlyToken } from '~/logic/tokens/store/actions/fetchTokens' import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' import { EMPTY_DATA } from '~/logic/wallets/ethTransactions' +import { type Token } from '~/logic/tokens/store/model/token' import { formatAmount } from '~/logic/tokens/utils/formatAmount' import { getWeb3 } from '~/logic/wallets/getWeb3' import { TX_NOTIFICATION_TYPES } from '~/logic/safe/transactions' -import { isEther } from '~/logic/tokens/utils/tokenHelpers' +import { ETH_ADDRESS } from '~/logic/tokens/utils/tokenHelpers' import ArrowDown from '../assets/arrow-down.svg' import { styles } from './style' @@ -35,6 +37,7 @@ type Props = { safeName: string, ethBalance: string, tx: Object, + tokens: List, createTransaction: Function, enqueueSnackbar: Function, closeSnackbar: Function, @@ -48,13 +51,15 @@ const ReviewTx = ({ safeName, ethBalance, tx, + tokens, createTransaction, enqueueSnackbar, closeSnackbar, }: Props) => { const [gasCosts, setGasCosts] = useState('< 0.001') - const isSendingETH = isEther(tx.token.symbol) - const txRecipient = isSendingETH ? tx.recipientAddress : tx.token.address + const txToken = tokens.find((token) => token.address === tx.token) + const isSendingETH = txToken.address === ETH_ADDRESS + const txRecipient = isSendingETH ? tx.recipientAddress : txToken.address useEffect(() => { let isCurrent = true @@ -65,7 +70,7 @@ const ReviewTx = ({ if (!isSendingETH) { const StandardToken = await getHumanFriendlyToken() - const tokenInstance = await StandardToken.at(tx.token.address) + const tokenInstance = await StandardToken.at(txToken.address) txData = tokenInstance.contract.methods.transfer(tx.recipientAddress, 0).encodeABI() } @@ -92,7 +97,7 @@ const ReviewTx = ({ if (!isSendingETH) { const HumanFriendlyToken = await getHumanFriendlyToken() - const tokenInstance = await HumanFriendlyToken.at(tx.token.address) + const tokenInstance = await HumanFriendlyToken.at(txToken.address) const decimals = await tokenInstance.decimals() txAmount = new BigNumber(tx.amount).times(10 ** decimals.toNumber()).toString() @@ -162,11 +167,11 @@ const ReviewTx = ({ - {tx.token.name} + {txToken.name} {tx.amount} {' '} - {tx.token.symbol} + {txToken.symbol} diff --git a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx index 976908ed..3414ca22 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx +++ b/src/routes/safe/components/Balances/SendModal/screens/SendFunds/index.jsx @@ -43,9 +43,7 @@ type Props = { const formMutators = { setMax: (args, state, utils) => { - const { token } = state.formState.values - - utils.changeValue(state, 'amount', () => token && token.balance) + utils.changeValue(state, 'amount', () => args[0]) }, onTokenChange: (args, state, utils) => { utils.changeValue(state, 'amount', () => '') @@ -156,7 +154,7 @@ const SendFunds = ({ Amount - + mutators.setMax(selectedTokenRecord.balance)}> Send max diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx index 0764066a..1d90251f 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.jsx @@ -90,31 +90,30 @@ const ReviewRemoveOwner = ({ {owners.map( - (owner) => - owner.address !== ownerAddress && ( - - - - - - - - - {owner.name} + (owner) => owner.address !== ownerAddress && ( + + + + + + + + + {owner.name} + + + + {owner.address} - - - {owner.address} - - - - + + - - - - - ), + + + + + + ), )} From 0dd2ad5f5d8c16e56ec883792fea8cfa21a8fc0a Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Tue, 15 Oct 2019 18:03:22 +0400 Subject: [PATCH 31/42] Fix gas calculation error when confirming tx because of the modal premount, improve cancelled tx detection --- src/logic/safe/transactions/gasNew.js | 2 +- .../TxsTable/ExpandedTx/index.jsx | 22 ++++++++++--------- src/routes/safe/container/selector.js | 5 +++-- 3 files changed, 16 insertions(+), 13 deletions(-) diff --git a/src/logic/safe/transactions/gasNew.js b/src/logic/safe/transactions/gasNew.js index e7395d92..a1aff18c 100644 --- a/src/logic/safe/transactions/gasNew.js +++ b/src/logic/safe/transactions/gasNew.js @@ -21,7 +21,7 @@ export const estimateTxGasCosts = async ( const nonce = await safeInstance.methods.nonce().call() const threshold = await safeInstance.methods.getThreshold().call() - const isExecution = (tx && tx.confirmations.size === threshold) || preApprovingOwner || threshold === '1' + const isExecution = (tx && tx.confirmations.size === threshold) || !!preApprovingOwner || threshold === '1' let txData if (isExecution) { diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.jsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.jsx index 461fdbf3..d2380a28 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.jsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.jsx @@ -123,16 +123,18 @@ const ExpandedTx = ({ tx={tx} safeAddress={safeAddress} /> - + {openModal === 'approveTx' && ( + + )} ) } diff --git a/src/routes/safe/container/selector.js b/src/routes/safe/container/selector.js index aafa2b79..259035d0 100644 --- a/src/routes/safe/container/selector.js +++ b/src/routes/safe/container/selector.js @@ -115,8 +115,9 @@ const extendedTransactionsSelector: Selector transaction.nonce === tx.nonce - && isAfter(parseISO(transaction.submissionDate), parseISO(tx.submissionDate)), + (transaction) => (transaction.nonce === tx.nonce + && isAfter(parseISO(transaction.submissionDate), parseISO(tx.submissionDate))) + || transaction.nonce > tx.nonce, ) if (replacementTransaction) { extendedTx = tx.set('cancelled', true) From c5a1ece144b602b0e65a9375779f58d2f99dc059 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Tue, 15 Oct 2019 19:12:34 +0400 Subject: [PATCH 32/42] Add owner flow gas estimations WIP --- .../ManageOwners/AddOwnerModal/index.jsx | 1 + .../AddOwnerModal/screens/Review/index.jsx | 39 ++++++++++++++++++- 2 files changed, 38 insertions(+), 2 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.jsx index ca2c0acd..b7b21737 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/index.jsx @@ -146,6 +146,7 @@ const AddOwner = ({ safeName={safeName} owners={owners} values={values} + safeAddress={safeAddress} onClickBack={onClickBack} onSubmit={onAddOwner} /> diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx index 9814080a..a046235c 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx @@ -1,5 +1,5 @@ // @flow -import React from 'react' +import React, { useState, useEffect } from 'react' import { List } from 'immutable' import classNames from 'classnames' import { withStyles } from '@material-ui/core/styles' @@ -7,6 +7,7 @@ import Close from '@material-ui/icons/Close' import IconButton from '@material-ui/core/IconButton' import Identicon from '~/components/Identicon' import EtherscanBtn from '~/components/EtherscanBtn' +import { getGnosisSafeInstanceAt } from '~/logic/contracts/safeContracts' import CopyBtn from '~/components/CopyBtn' import Paragraph from '~/components/layout/Paragraph' import Row from '~/components/layout/Row' @@ -15,6 +16,9 @@ import Button from '~/components/layout/Button' import Block from '~/components/layout/Block' import Hairline from '~/components/layout/Hairline' import type { Owner } from '~/routes/safe/store/models/owner' +import { getWeb3 } from '~/logic/wallets/getWeb3' +import { formatAmount } from '~/logic/tokens/utils/formatAmount' +import { estimateTxGasCosts } from '~/logic/safe/transactions/gasNew' import { styles } from './style' export const ADD_OWNER_SUBMIT_BTN_TEST_ID = 'add-owner-submit-btn' @@ -27,11 +31,38 @@ type Props = { values: Object, onClickBack: Function, onSubmit: Function, + safeAddress: string, } const ReviewAddOwner = ({ - classes, onClose, safeName, owners, values, onClickBack, onSubmit, + classes, onClose, safeName, owners, values, onClickBack, onSubmit, safeAddress, }: Props) => { + const [gasCosts, setGasCosts] = useState('< 0.001') + useEffect(() => { + let isCurrent = true + const estimateGas = async () => { + const web3 = getWeb3() + const { fromWei, toBN } = web3.utils + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) + const txData = safeInstance.contract.methods + .addOwnerWithThreshold(values.ownerAddress, values.threshold) + .encodeABI() + + const estimatedGasCosts = await estimateTxGasCosts(safeAddress, safeAddress, txData) + const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') + const formattedGasCosts = formatAmount(gasCostsAsEth) + if (isCurrent) { + setGasCosts(formattedGasCosts) + } + } + + estimateGas() + + return () => { + isCurrent = false + } + }, []) + const handleSubmit = () => { onSubmit() } @@ -135,6 +166,10 @@ const ReviewAddOwner = ({ + + {gasCosts} + + - - - -) + + + + + + + + Details + + + + + Safe name + + + {safeName} + + + + + Any transaction requires the confirmation of: + + + {`${threshold} out of ${owners.size} owner(s)`} + + + + + + + + {`${owners.size} Safe owner(s)`} + + + + {owners.map( + (owner) => owner.address !== ownerAddress && ( + + + + + + + + + {owner.name} + + + + {owner.address} + + + + + + + + + + ), + )} + + + REMOVING OWNER ↓ + + + + + + + + + + + {ownerName} + + + + {ownerAddress} + + + + + + + + + + ADDING NEW OWNER ↓ + + + + + + + + + + + {values.ownerName} + + + + {values.ownerAddress} + + + + + + + + + + + + + + Yooo + + + + + + + + ) +} export default withStyles(styles)(ReviewRemoveOwner) From 44b7282865a44c0ca540b8c645b0edc0ecf88a3f Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Wed, 16 Oct 2019 18:03:01 +0400 Subject: [PATCH 35/42] ReplaceOwnerModal gas estimations --- .../AddOwnerModal/screens/Review/index.jsx | 16 +++++----- .../screens/Review/index.jsx | 30 +++++++++++++------ .../ReplaceOwnerModal/screens/Review/style.js | 5 ++++ 3 files changed, 33 insertions(+), 18 deletions(-) diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx index 6f3f6791..8fc42b58 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.jsx @@ -167,15 +167,13 @@ const ReviewAddOwner = ({ - - - - You're about to create a transaction and will have to confirm it with your currently connected wallet. -
- {`Make sure you have ${gasCosts} (fee price) ETH in this wallet to fund this confirmation.`} -
-
-
+ + + You're about to create a transaction and will have to confirm it with your currently connected wallet. +
+ {`Make sure you have ${gasCosts} (fee price) ETH in this wallet to fund this confirmation.`} +
+