estimate tx when executing transaction with threshold > 1

This commit is contained in:
Mikhail Mikheev 2019-10-11 18:43:37 +04:00
parent 4aa48d8fce
commit 1e5958b8ea
7 changed files with 68 additions and 40 deletions

View File

@ -68,8 +68,8 @@
"web3": "1.2.1" "web3": "1.2.1"
}, },
"devDependencies": { "devDependencies": {
"@babel/cli": "7.6.3", "@babel/cli": "7.6.4",
"@babel/core": "7.6.3", "@babel/core": "7.6.4",
"@babel/plugin-proposal-class-properties": "7.5.5", "@babel/plugin-proposal-class-properties": "7.5.5",
"@babel/plugin-proposal-decorators": "7.6.0", "@babel/plugin-proposal-decorators": "7.6.0",
"@babel/plugin-proposal-do-expressions": "7.6.0", "@babel/plugin-proposal-do-expressions": "7.6.0",
@ -136,12 +136,12 @@
"storybook-host": "5.1.0", "storybook-host": "5.1.0",
"storybook-router": "^0.3.4", "storybook-router": "^0.3.4",
"style-loader": "1.0.0", "style-loader": "1.0.0",
"truffle": "5.0.39", "truffle": "5.0.40",
"truffle-contract": "4.0.31", "truffle-contract": "4.0.31",
"truffle-solidity-loader": "0.1.32", "truffle-solidity-loader": "0.1.32",
"uglifyjs-webpack-plugin": "2.2.0", "uglifyjs-webpack-plugin": "2.2.0",
"url-loader": "2.2.0", "url-loader": "2.2.0",
"webpack": "4.41.0", "webpack": "4.41.1",
"webpack-bundle-analyzer": "3.5.2", "webpack-bundle-analyzer": "3.5.2",
"webpack-cli": "3.3.9", "webpack-cli": "3.3.9",
"webpack-dev-server": "3.8.2", "webpack-dev-server": "3.8.2",

View File

@ -1,8 +1,8 @@
// @flow // @flow
import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json' import GnosisSafeSol from '@gnosis.pm/safe-contracts/build/contracts/GnosisSafe.json'
import { List } from 'immutable' import { type Transaction } from '~/routes/safe/store/models/transaction'
import { type Confirmation } from '~/routes/safe/store/models/confirmation'
import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3' import { getWeb3, getAccountFrom } from '~/logic/wallets/getWeb3'
import { generateSignaturesFromTxConfirmations } from '~/routes/safe/store/actions/processTransaction'
import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions' import { calculateGasOf, calculateGasPrice } from '~/logic/wallets/ethTransactions'
import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses' import { ZERO_ADDRESS } from '~/logic/wallets/ethAddresses'
import { CALL } from '.' import { CALL } from '.'
@ -11,7 +11,8 @@ export const estimateTxGasCosts = async (
safeAddress: string, safeAddress: string,
to: string, to: string,
data: string, data: string,
confirmations?: List<Confirmation>, tx?: Transaction,
preApprovingOwner?: string,
): Promise<number> => { ): Promise<number> => {
try { try {
const web3 = getWeb3() const web3 = getWeb3()
@ -20,18 +21,19 @@ export const estimateTxGasCosts = async (
const nonce = await safeInstance.methods.nonce().call() const nonce = await safeInstance.methods.nonce().call()
const threshold = await safeInstance.methods.getThreshold().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 let txData
if (isExecution) { if (isExecution) {
// https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
const signatures = confirmations const signatures = tx && tx.confirmations
|| `0x000000000000000000000000${from.replace( ? generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
: `0x000000000000000000000000${from.replace(
'0x', '0x',
'', '',
)}000000000000000000000000000000000000000000000000000000000000000001` )}000000000000000000000000000000000000000000000000000000000000000001`
txData = await safeInstance.methods 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() .encodeABI()
} else { } else {
const txHash = await safeInstance.methods const txHash = await safeInstance.methods
@ -47,7 +49,8 @@ export const estimateTxGasCosts = async (
return gas * parseInt(gasPrice, 10) return gas * parseInt(gasPrice, 10)
} catch (err) { } 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 return 10000
} }

View File

@ -56,6 +56,6 @@ export const calculateGasOf = async (data: Object, from: string, to: string) =>
return gas * 2 return gas * 2
} catch (err) { } catch (err) {
return Promise.reject(new Error(err)) return Promise.reject(err)
} }
} }

View File

@ -63,11 +63,11 @@ const ChangeThreshold = ({
} }
}, []) }, [])
const handleSubmit = async (values) => { const handleSubmit = (values) => {
const newThreshold = values[THRESHOLD_FIELD_NAME] const newThreshold = values[THRESHOLD_FIELD_NAME]
await onChangeThreshold(newThreshold)
onClose() onClose()
onChangeThreshold(newThreshold)
} }
return ( return (

View File

@ -76,7 +76,13 @@ const ApproveTxModal = ({
const web3 = getWeb3() const web3 = getWeb3()
const { fromWei, toBN } = web3.utils 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 gasCostsAsEth = fromWei(toBN(estimatedGasCosts), 'ether')
const formattedGasCosts = formatAmount(gasCostsAsEth) const formattedGasCosts = formatAmount(gasCostsAsEth)
if (isCurrent) { if (isCurrent) {
@ -140,7 +146,9 @@ const ApproveTxModal = ({
</Row> </Row>
<Row> <Row>
<Paragraph> <Paragraph>
{`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.`}
</Paragraph> </Paragraph>
</Row> </Row>
</Block> </Block>

View File

@ -1,5 +1,7 @@
// @flow // @flow
import type { Dispatch as ReduxDispatch, GetState } from 'redux' 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 { type Transaction } from '~/routes/safe/store/models/transaction'
import { userAccountSelector } from '~/logic/wallets/store/selectors' import { userAccountSelector } from '~/logic/wallets/store/selectors'
import fetchTransactions from '~/routes/safe/store/actions/fetchTransactions' 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://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
// https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26 // https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26
const generateSignaturesFromTxConfirmations = (tx: Transaction, preApprovingOwner?: string) => { export const generateSignaturesFromTxConfirmations = (confirmations: List<Confirmation>, preApprovingOwner?: string) => {
// The constant parts need to be sorted so that the recovered signers are sorted ascending // The constant parts need to be sorted so that the recovered signers are sorted ascending
// (natural order) by address (not checksummed). // (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) { if (preApprovingOwner) {
confirmedAdresses = confirmedAdresses.push(preApprovingOwner) confirmedAdresses = confirmedAdresses.push(preApprovingOwner)
@ -60,7 +62,7 @@ const processTransaction = (
const threshold = (await safeInstance.getThreshold()).toNumber() const threshold = (await safeInstance.getThreshold()).toNumber()
const shouldExecute = threshold === tx.confirmations.size || approveAndExecute 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 // https://gnosis-safe.readthedocs.io/en/latest/contracts/signatures.html#pre-validated-signatures
if (!sigs) { if (!sigs) {
sigs = `0x000000000000000000000000${from.replace( sigs = `0x000000000000000000000000${from.replace(

View File

@ -2,10 +2,10 @@
# yarn lockfile v1 # yarn lockfile v1
"@babel/cli@7.6.3": "@babel/cli@7.6.4":
version "7.6.3" version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.3.tgz#1b0c62098c8a5e01e4a4a59a52cba9682e7e0906" resolved "https://registry.yarnpkg.com/@babel/cli/-/cli-7.6.4.tgz#9b35a4e15fa7d8f487418aaa8229c8b0bc815f20"
integrity sha512-kWKOEeuylpa781yCeA5//eEx1u3WtLZqbi2VWXLKmb3QDPb5T2f7Yk311MK7bvvjR70dluAeiu4VXXsG1WwJsw== integrity sha512-tqrDyvPryBM6xjIyKKUwr3s8CzmmYidwgdswd7Uc/Cv0ogZcuS1TYQTLx/eWKP3UbJ6JxZAiYlBZabXm/rtRsQ==
dependencies: dependencies:
commander "^2.8.1" commander "^2.8.1"
convert-source-map "^1.1.0" convert-source-map "^1.1.0"
@ -15,7 +15,7 @@
mkdirp "^0.5.1" mkdirp "^0.5.1"
output-file-sync "^2.0.0" output-file-sync "^2.0.0"
slash "^2.0.0" slash "^2.0.0"
source-map "^0.6.1" source-map "^0.5.0"
optionalDependencies: optionalDependencies:
chokidar "^2.1.8" chokidar "^2.1.8"
@ -53,15 +53,15 @@
semver "^5.4.1" semver "^5.4.1"
source-map "^0.5.0" source-map "^0.5.0"
"@babel/core@7.6.3": "@babel/core@7.6.4":
version "7.6.3" version "7.6.4"
resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.3.tgz#44de824e89eaa089bb12da7337bc9bdff2ab68f9" resolved "https://registry.yarnpkg.com/@babel/core/-/core-7.6.4.tgz#6ebd9fe00925f6c3e177bb726a188b5f578088ff"
integrity sha512-QfQ5jTBgXLzJuo7Mo8bZK/ePywmgNRgk/UQykiKwEtZPiFIn8ZqE6jB+AnD1hbB1S2xQyL4//it5vuAUOVAMTw== integrity sha512-Rm0HGw101GY8FTzpWSyRbki/jzq+/PkNQJ+nSulrdY6gFGOsNseCqD6KHRYe2E+EdzuBdr2pxCp6s4Uk6eJ+XQ==
dependencies: dependencies:
"@babel/code-frame" "^7.5.5" "@babel/code-frame" "^7.5.5"
"@babel/generator" "^7.6.3" "@babel/generator" "^7.6.4"
"@babel/helpers" "^7.6.2" "@babel/helpers" "^7.6.2"
"@babel/parser" "^7.6.3" "@babel/parser" "^7.6.4"
"@babel/template" "^7.6.0" "@babel/template" "^7.6.0"
"@babel/traverse" "^7.6.3" "@babel/traverse" "^7.6.3"
"@babel/types" "^7.6.3" "@babel/types" "^7.6.3"
@ -71,7 +71,7 @@
lodash "^4.17.13" lodash "^4.17.13"
resolve "^1.3.2" resolve "^1.3.2"
semver "^5.4.1" 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": "@babel/core@^7.0.0", "@babel/core@^7.1.0", "@babel/core@^7.4.5":
version "7.5.5" version "7.5.5"
@ -124,6 +124,16 @@
lodash "^4.17.13" lodash "^4.17.13"
source-map "^0.6.1" 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": "@babel/helper-annotate-as-pure@^7.0.0":
version "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" 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" resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.6.3.tgz#9eff8b9c3eeae16a74d8d4ff30da2bd0d6f0487e"
integrity sha512-sUZdXlva1dt2Vw2RqbMkmfoImubO0D0gaCrNngV6Hi0DA4x3o4mlrq0tbfY0dZEUIccH8I6wQ4qgEtwcpOR6Qg== 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": "@babel/plugin-proposal-async-generator-functions@^7.2.0":
version "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" 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-external-compile "^1.0.15"
truffle-resolver "^5.0.15" truffle-resolver "^5.0.15"
truffle@5.0.39: truffle@5.0.40:
version "5.0.39" version "5.0.40"
resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.39.tgz#5710ba8f60a7184d9eb51d632308f2af0a2e8aff" resolved "https://registry.yarnpkg.com/truffle/-/truffle-5.0.40.tgz#3fb238f0e17662df871f862981bea4109e68bf9f"
integrity sha512-2a17t4o6r0rNMpeQXBc51nXigtIaP9/sU8N2zflaazvzYgDgLMZfqh/dir2mTfyybOsrR47NL310p+6+c8u8VA== integrity sha512-UO2bVpDJRzR1oF6LgdxdpqUHdbvYJxfaqEkVDUrziIRs6Sr7CkOzHew8hexUgkqxA7wtbDmM9FNcj+9Yoa5csA==
dependencies: dependencies:
app-module-path "^2.2.0" app-module-path "^2.2.0"
mocha "5.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-list-map "^2.0.0"
source-map "~0.6.1" source-map "~0.6.1"
webpack@4.41.0: webpack@4.41.1:
version "4.41.0" version "4.41.1"
resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.0.tgz#db6a254bde671769f7c14e90a1a55e73602fc70b" resolved "https://registry.yarnpkg.com/webpack/-/webpack-4.41.1.tgz#5388dd3047d680d5d382a84249fd4750e87372fd"
integrity sha512-yNV98U4r7wX1VJAj5kyMsu36T8RPPQntcb5fJLOsMz/pt/WrKC0Vp1bAlqPLkA1LegSwQwf6P+kAbyhRKVQ72g== integrity sha512-ak7u4tUu/U63sCVxA571IuPZO/Q0pZ9cEXKg+R/woxkDzVovq57uB6L2Hlg/pC8LCU+TWpvtcYwsstivQwMJmw==
dependencies: dependencies:
"@webassemblyjs/ast" "1.8.5" "@webassemblyjs/ast" "1.8.5"
"@webassemblyjs/helper-module-context" "1.8.5" "@webassemblyjs/helper-module-context" "1.8.5"