From 1e5958b8ea17080c8550179f0039d21b11b16425 Mon Sep 17 00:00:00 2001 From: Mikhail Mikheev Date: Fri, 11 Oct 2019 18:43:37 +0400 Subject: [PATCH] estimate tx when executing transaction with threshold > 1 --- package.json | 8 +-- src/logic/safe/transactions/gasNew.js | 19 ++++--- src/logic/wallets/ethTransactions.js | 2 +- .../ChangeThreshold/index.jsx | 4 +- .../ExpandedTx/ApproveTxModal/index.jsx | 12 +++- .../safe/store/actions/processTransaction.js | 8 ++- yarn.lock | 55 ++++++++++++------- 7 files changed, 68 insertions(+), 40 deletions(-) diff --git a/package.json b/package.json index 630aba60..d06dad5a 100644 --- a/package.json +++ b/package.json @@ -68,8 +68,8 @@ "web3": "1.2.1" }, "devDependencies": { - "@babel/cli": "7.6.3", - "@babel/core": "7.6.3", + "@babel/cli": "7.6.4", + "@babel/core": "7.6.4", "@babel/plugin-proposal-class-properties": "7.5.5", "@babel/plugin-proposal-decorators": "7.6.0", "@babel/plugin-proposal-do-expressions": "7.6.0", @@ -136,12 +136,12 @@ "storybook-host": "5.1.0", "storybook-router": "^0.3.4", "style-loader": "1.0.0", - "truffle": "5.0.39", + "truffle": "5.0.40", "truffle-contract": "4.0.31", "truffle-solidity-loader": "0.1.32", "uglifyjs-webpack-plugin": "2.2.0", "url-loader": "2.2.0", - "webpack": "4.41.0", + "webpack": "4.41.1", "webpack-bundle-analyzer": "3.5.2", "webpack-cli": "3.3.9", "webpack-dev-server": "3.8.2", diff --git a/src/logic/safe/transactions/gasNew.js b/src/logic/safe/transactions/gasNew.js index da8db6a5..eb359214 100644 --- a/src/logic/safe/transactions/gasNew.js +++ b/src/logic/safe/transactions/gasNew.js @@ -1,8 +1,8 @@ // @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 { type Transaction } from '~/routes/safe/store/models/transaction' import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' +import { generateSignaturesFromTxConfirmations } from '~/routes/safe/store/actions/processTransaction' import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' import { CALL } from '.' @@ -11,7 +11,8 @@ export const estimateTxGasCosts = async ( safeAddress: string, to: string, data: string, - confirmations?: List, + tx?: Transaction, + preApprovingOwner?: string, ): Promise => { try { const web3 = getWeb3() @@ -20,18 +21,19 @@ export const estimateTxGasCosts = async ( const nonce = await safeInstance.methods.nonce().call() const threshold = await safeInstance.methods.getThreshold().call() - const isExecution = (confirmations && confirmations.size) || threshold === '1' + const isExecution = (tx && tx.confirmations.size) || threshold === '1' let txData if (isExecution) { // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures - const signatures = confirmations - || `0x000000000000000000000000${from.replace( + const signatures = tx && tx.confirmations + ? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner) + : `0x000000000000000000000000${from.replace( '0x', '', )}000000000000000000000000000000000000000000000000000000000000000001` txData = await safeInstance.methods - .execTransaction(to, 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, signatures) + .execTransaction(to, tx ? tx.value : 0, data, CALL, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, signatures) .encodeABI() } else { const txHash = await safeInstance.methods @@ -47,7 +49,8 @@ export const estimateTxGasCosts = async ( return gas * parseInt(gasPrice, 10) } catch (err) { - console.error(`Error while estimating transaction execution gas costs: ${err}`) + console.error('Error while estimating transaction execution gas costs:') + console.error(err) return 10000 } diff --git a/src/logic/wallets/ethTransactions.js b/src/logic/wallets/ethTransactions.js index 7ee0cf33..f8673ad6 100644 --- a/src/logic/wallets/ethTransactions.js +++ b/src/logic/wallets/ethTransactions.js @@ -56,6 +56,6 @@ export const calculateGasOf = async (data: Object, from: string, to: string) => return gas * 2 } catch (err) { - return Promise.reject(new Error(err)) + return Promise.reject(err) } } diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx index 37ab5e8f..653d7f96 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.jsx @@ -63,11 +63,11 @@ const ChangeThreshold = ({ } }, []) - const handleSubmit = async (values) => { + const handleSubmit = (values) => { const newThreshold = values[THRESHOLD_FIELD_NAME] - await onChangeThreshold(newThreshold) onClose() + onChangeThreshold(newThreshold) } return ( 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 d470fd98..a8dadc3f 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.jsx @@ -76,7 +76,13 @@ const ApproveTxModal = ({ const web3 = getWeb3() const { fromWei, toBN } = web3.utils - const estimatedGasCosts = await estimateTxGasCosts(safeAddress, tx.recipient, tx.data, tx.confirmations) + const estimatedGasCosts = await estimateTxGasCosts( + safeAddress, + tx.recipient, + tx.data, + tx, + approveAndExecute ? userAddress : undefined, + ) const gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether') const formattedGasCosts = formatAmount(gasCostsAsEth) if (isCurrent) { @@ -140,7 +146,9 @@ 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.`} + {`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.`} diff --git a/src/routes/safe/store/actions/processTransaction.js b/src/routes/safe/store/actions/processTransaction.js index 06ad45e4..a9342520 100644 --- a/src/routes/safe/store/actions/processTransaction.js +++ b/src/routes/safe/store/actions/processTransaction.js @@ -1,5 +1,7 @@ // @flow import type { Dispatch as ReduxDispatch, GetState } from 'redux' +import { List } from 'immutable' +import { type Confirmation } from '~/routes/safe/store/models/confirmation' import { type Transaction } from '~/routes/safe/store/models/transaction' import { userAccountSelector } from '~/logic/wallets/store/selectors' import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions' @@ -24,10 +26,10 @@ import { getErrorMessage } from '~/test/utils/ethereumErrors' // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures // https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26 -const generateSignaturesFromTxConfirmations = (tx: Transaction, preApprovingOwner?: string) => { +export const generateSignaturesFromTxConfirmations = (confirmations: List, preApprovingOwner?: string) => { // The constant parts need to be sorted so that the recovered signers are sorted ascending // (natural order) by address (not checksummed). - let confirmedAdresses = tx.confirmations.map((conf) => conf.owner.address) + let confirmedAdresses = confirmations.map((conf) => conf.owner.address) if (preApprovingOwner) { confirmedAdresses = confirmedAdresses.push(preApprovingOwner) @@ -60,7 +62,7 @@ const processTransaction = ( const threshold = (await safeInstance.getThreshold()).toNumber() const shouldExecute = threshold === tx.confirmations.size || approveAndExecute - let sigs = generateSignaturesFromTxConfirmations(tx, approveAndExecute && userAddress) + let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, approveAndExecute && userAddress) // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures if (!sigs) { sigs = `0x000000000000000000000000${from.replace( diff --git a/yarn.lock b/yarn.lock index 39b1958a..512d3f1b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2,10 +2,10 @@ # yarn lockfile v1 -"@babel/cli@7.6.3": - version "7.6.3" - resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.3.tgz#1b0c62098c8a5e01e4a4a59a52cba9682e7e0906" - integrity sha512-kWKOEeuylpa781yCeA5//eEx1u3WtLZqbi2VWXLKmb3QDPb5T2f7Yk311MK7bvvjR70dluAeiu4VXXsG1WwJsw== +"@babel/cli@7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.4.tgz#9b35a4e15fa7d8f487418aaa8229c8b0bc815f20" + integrity sha512-tqrDyvPryBM6xjIyKKUwr3s8CzmmYidwgdswd7Uc/Cv0ogZcuS1TYQTLx/eWKP3UbJ6JxZAiYlBZabXm/rtRsQ== dependencies: commander "^2.8.1" convert-source-map "^1.1.0" @@ -15,7 +15,7 @@ mkdirp "^0.5.1" output-file-sync "^2.0.0" slash "^2.0.0" - source-map "^0.6.1" + source-map "^0.5.0" optionalDependencies: chokidar "^2.1.8" @@ -53,15 +53,15 @@ semver "^5.4.1" source-map "^0.5.0" -"@babel/core@7.6.3": - version "7.6.3" - resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.3.tgz#44de824e89eaa089bb12da7337bc9bdff2ab68f9" - integrity sha512-QfQ5jTBgXLzJuo7Mo8bZK/ePywmgNRgk/UQykiKwEtZPiFIn8ZqE6jB+AnD1hbB1S2xQyL4//it5vuAUOVAMTw== +"@babel/core@7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff" + integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ== dependencies: "@babel/code-frame" "^7.5.5" - "@babel/generator" "^7.6.3" + "@babel/generator" "^7.6.4" "@babel/helpers" "^7.6.2" - "@babel/parser" "^7.6.3" + "@babel/parser" "^7.6.4" "@babel/template" "^7.6.0" "@babel/traverse" "^7.6.3" "@babel/types" "^7.6.3" @@ -71,7 +71,7 @@ lodash "^4.17.13" resolve "^1.3.2" semver "^5.4.1" - source-map "^0.6.1" + source-map "^0.5.0" "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5": version "7.5.5" @@ -124,6 +124,16 @@ lodash "^4.17.13" source-map "^0.6.1" +"@babel/generator@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/generator/-/generator-7.6.4.tgz#a4f8437287bf9671b07f483b76e3bb731bc97671" + integrity sha512-jsBuXkFoZxk0yWLyGI9llT9oiQ2FeTASmRFE32U+aaDTfoE92t78eroO7PTpU/OrYq38hlcDM6vbfLDaOLy+7w== + dependencies: + "@babel/types" "^7.6.3" + jsesc "^2.5.1" + lodash "^4.17.13" + source-map "^0.5.0" + "@babel/helper-annotate-as-pure@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.0.0.tgz#323d39dd0b50e10c7c06ca7d7638e6864d8c5c32" @@ -358,6 +368,11 @@ resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.3.tgz#9eff8b9c3eeae16a74d8d4ff30da2bd0d6f0487e" integrity sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg== +"@babel/parser@^7.6.4": + version "7.6.4" + resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.4.tgz#cb9b36a7482110282d5cb6dd424ec9262b473d81" + integrity sha512-D8RHPW5qd0Vbyo3qb+YjO5nvUVRTXFLQ/FsDxJU2Nqz4uB5EnUN0ZQSEYpvTIbRuttig1XbHWU5oMeQwQSAA+A== + "@babel/plugin-proposal-async-generator-functions@^7.2.0": version "7.2.0" resolved "https://registry.yarnpkg.com/@babel/plugin-proposal-async-generator-functions/-/plugin-proposal-async-generator-functions-7.2.0.tgz#b289b306669dce4ad20b0252889a15768c9d417e" @@ -17994,10 +18009,10 @@ truffle-workflow-compile@^2.1.3: truffle-external-compile "^1.0.15" truffle-resolver "^5.0.15" -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== +truffle@5.0.40: + version "5.0.40" + resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.40.tgz#3fb238f0e17662df871f862981bea4109e68bf9f" + integrity sha512-UO2bVpDJRzR1oF6LgdxdpqUHdbvYJxfaqEkVDUrziIRs6Sr7CkOzHew8hexUgkqxA7wtbDmM9FNcj+9Yoa5csA== dependencies: app-module-path "^2.2.0" mocha "5.2.0" @@ -19855,10 +19870,10 @@ webpack-sources@^1.4.0, webpack-sources@^1.4.1: source-list-map "^2.0.0" source-map "~0.6.1" -webpack@4.41.0: - version "4.41.0" - resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" - integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== +webpack@4.41.1: + version "4.41.1" + resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.1.tgz#5388dd3047d680d5d382a84249fd4750e87372fd" + integrity sha512-ak7u4tUu/U63sCVxA571IuPZO/Q0pZ9cEXKg+R/woxkDzVovq57uB6L2Hlg/pC8LCU+TWpvtcYwsstivQwMJmw== dependencies: "@webassemblyjs/ast" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5"