From f768d4c84c0160538a2ce5328a4ca0fbd62bea5a Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Mon, 4 Jan 2021 13:45:23 -0300 Subject: [PATCH 01/25] Refactor proccessTransaction to use getPreValidatedSignatures method --- src/logic/safe/store/actions/processTransaction.ts | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts index eced5a75c8..b08c966cf4 100644 --- a/src/logic/safe/store/actions/processTransaction.ts +++ b/src/logic/safe/store/actions/processTransaction.ts @@ -22,6 +22,7 @@ import { storeExecutedTx, storeSignedTx, storeTx } from 'src/logic/safe/store/ac import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { Dispatch, DispatchReturn } from './types' +import { getPreValidatedSignatures } from 'src/logic/safe/transactions/gas' interface ProcessTransactionArgs { approveAndExecute: boolean @@ -54,12 +55,9 @@ const processTransaction = ({ const safeVersion = await getCurrentSafeVersion(safeInstance) let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, approveAndExecute && userAddress) - // https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures + if (!sigs) { - sigs = `0x000000000000000000000000${from.replace( - '0x', - '', - )}000000000000000000000000000000000000000000000000000000000000000001` + sigs = getPreValidatedSignatures(from) } const notificationsQueue = getNotificationsFromTxType(notifiedTransaction, tx.origin) From 694e450b0298d721c8edc6a2df3ea366d23c1910 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Mon, 4 Jan 2021 13:48:18 -0300 Subject: [PATCH 02/25] Fix default export of ApproveTxModal --- .../Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx | 4 +--- .../components/Transactions/TxsTable/ExpandedTx/index.tsx | 2 +- 2 files changed, 2 insertions(+), 4 deletions(-) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx index 72f5708bb0..40060bfffd 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx @@ -63,7 +63,7 @@ type Props = { } const { nativeCoin } = getNetworkInfo() -const ApproveTxModal = ({ +export const ApproveTxModal = ({ canExecute, isCancelTx = false, isOpen, @@ -170,5 +170,3 @@ const ApproveTxModal = ({ ) } - -export default ApproveTxModal diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx index e8febd30eb..274acf47e1 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/index.tsx @@ -3,7 +3,7 @@ import React, { ReactElement, useMemo, useState } from 'react' import { useSelector } from 'react-redux' import { EthHashInfo } from '@gnosis.pm/safe-react-components' -import ApproveTxModal from './ApproveTxModal' +import { ApproveTxModal } from './ApproveTxModal' import OwnersColumn from './OwnersColumn' import { RejectTxModal } from './RejectTxModal' import TxDescription from './TxDescription' From 2c5f21d14babf79b94d51644c19422654c209eac Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 5 Jan 2021 10:06:15 -0300 Subject: [PATCH 03/25] Rename estimateExecTransactionGas to estimateGasForTransactionCreation Remove estimateTransactionGas from gas.ts --- .../safe/store/actions/createTransaction.ts | 5 +- src/logic/safe/transactions/gas.ts | 60 +------------------ 2 files changed, 6 insertions(+), 59 deletions(-) diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index 5052b5cca3..51dcd1fe74 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -14,7 +14,7 @@ import { saveTxToHistory, tryOffchainSigning, } from 'src/logic/safe/transactions' -import { estimateExecTransactionGas, getPreValidatedSignatures } from 'src/logic/safe/transactions/gas' +import { estimateGasForTransactionCreation, getPreValidatedSignatures } from 'src/logic/safe/transactions/gas' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' @@ -90,7 +90,8 @@ const createTransaction = ( const nonce = await getNewTxNonce(txNonce?.toString(), lastTx, safeInstance) const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx) const safeVersion = await getCurrentSafeVersion(safeInstance) - const safeTxGas = safeTxGasArg || (await estimateExecTransactionGas(safeAddress, txData, to, valueInWei, operation)) + const safeTxGas = + safeTxGasArg || (await estimateGasForTransactionCreation(safeAddress, txData, to, valueInWei, operation)) const sigs = getPreValidatedSignatures(from) diff --git a/src/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts index 2f98bb853a..80cea1b106 100644 --- a/src/logic/safe/transactions/gas.ts +++ b/src/logic/safe/transactions/gas.ts @@ -1,9 +1,7 @@ import { BigNumber } from 'bignumber.js' -import { CALL } from '.' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' -import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' -import { EMPTY_DATA, calculateGasOf } from 'src/logic/wallets/ethTransactions' -import { getAccountFrom, getWeb3 } from 'src/logic/wallets/getWeb3' +import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' +import { getWeb3 } from 'src/logic/wallets/getWeb3' import { sameString } from 'src/utils/strings' // Receives the response data of the safe method requiredTxGas() and parses it to get the gas amount @@ -31,58 +29,6 @@ export const getPreValidatedSignatures = (from: string): string => { )}000000000000000000000000000000000000000000000000000000000000000001` } -export type TransactionEstimationProps = { - txData: string - safeAddress: string - txRecipient: string - isExecution: boolean - txAmount?: string - operation?: number -} - -export const estimateTransactionGas = async ({ - txAmount, - txData, - txRecipient, - isExecution, - safeAddress, - operation = CALL, -}: TransactionEstimationProps): Promise => { - const web3 = getWeb3() - const from = await getAccountFrom(web3) - - if (!from) { - return 0 - } - - if (isExecution) { - // Gas of executing a transaction within the safe (threshold reached and transaction executed) - return await estimateExecTransactionGas(safeAddress, txData, txRecipient, txAmount || '0', operation) - } - - const safeInstance = await getGnosisSafeInstanceAt(safeAddress) - const nonce = await safeInstance.methods.nonce().call() - const txHash = await safeInstance.methods - .getTransactionHash( - txRecipient, - txAmount || '0', - txData, - operation as number, - 0, - 0, - 0, - ZERO_ADDRESS, - ZERO_ADDRESS, - nonce, - ) - .call({ - from, - }) - // Gas of approving the transaction (threshold not reached or user did not executed the transaction) - const approveTransactionTxData = await safeInstance.methods.approveHash(txHash).encodeABI() - return await calculateGasOf(approveTransactionTxData, from, safeAddress) -} - // Parses the result from the error message (GETH, OpenEthereum/Parity and Nethermind) and returns the data value export const getDataFromNodeErrorMessage = (errorMessage: string): string | undefined => { // Replace illegal characters that often comes within the error string (like � for example) @@ -185,7 +131,7 @@ const calculateMinimumGasForTransaction = async ( return 0 } -export const estimateExecTransactionGas = async ( +export const estimateGasForTransactionCreation = async ( safeAddress: string, data: string, to: string, From 40ec65d68e24b8e03990e51d2d9ac9154828f53b Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 5 Jan 2021 13:52:15 -0300 Subject: [PATCH 04/25] Make estimateGasForTransactionCreation throw error instead of 0 gas --- src/logic/safe/store/actions/createTransaction.ts | 9 +++++++-- src/logic/safe/transactions/gas.ts | 2 +- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index 51dcd1fe74..97969aec5f 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -90,8 +90,13 @@ const createTransaction = ( const nonce = await getNewTxNonce(txNonce?.toString(), lastTx, safeInstance) const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx) const safeVersion = await getCurrentSafeVersion(safeInstance) - const safeTxGas = - safeTxGasArg || (await estimateGasForTransactionCreation(safeAddress, txData, to, valueInWei, operation)) + let safeTxGas + try { + safeTxGas = + safeTxGasArg || (await estimateGasForTransactionCreation(safeAddress, txData, to, valueInWei, operation)) + } catch (error) { + safeTxGas = safeTxGasArg || 0 + } const sigs = getPreValidatedSignatures(from) diff --git a/src/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts index 80cea1b106..09d1a63b5e 100644 --- a/src/logic/safe/transactions/gas.ts +++ b/src/logic/safe/transactions/gas.ts @@ -163,6 +163,6 @@ export const estimateGasForTransactionCreation = async ( ) } catch (error) { console.info('Error calculating tx gas estimation', error.message) - return 0 + throw error } } From a1ac679a04e23523f127583b85dec79f7968a3e9 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 5 Jan 2021 14:14:38 -0300 Subject: [PATCH 05/25] Adds estimateGasForTransactionExecution and estimateGasForTransactionApproval to gas.ts --- src/logic/safe/transactions/gas.ts | 126 ++++++++++++++++++++++++++++- 1 file changed, 125 insertions(+), 1 deletion(-) diff --git a/src/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts index 09d1a63b5e..d05b270ed9 100644 --- a/src/logic/safe/transactions/gas.ts +++ b/src/logic/safe/transactions/gas.ts @@ -1,8 +1,10 @@ import { BigNumber } from 'bignumber.js' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' -import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' +import { calculateGasOf, EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { getWeb3 } from 'src/logic/wallets/getWeb3' import { sameString } from 'src/utils/strings' +import { CALL } from './send' +import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' // Receives the response data of the safe method requiredTxGas() and parses it to get the gas amount const parseRequiredTxGasResponse = (data: string): number => { @@ -166,3 +168,125 @@ export const estimateGasForTransactionCreation = async ( throw error } } + +type TransactionExecutionEstimationProps = { + txData: string + safeAddress: string + txRecipient: string + txAmount?: string + operation?: number + gasPrice?: string + gasToken?: string + refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin). + safeTxGas?: string + from: string +} + +export const estimateGasForTransactionExecution = async ({ + safeAddress, + txRecipient, + txAmount = '0', + txData, + operation = CALL, + from, + gasPrice = '0', + gasToken = ZERO_ADDRESS, + refundReceiver = ZERO_ADDRESS, + safeTxGas = '0', +}: TransactionExecutionEstimationProps): Promise => { + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) + const sigs = getPreValidatedSignatures(from) + const baseGas = await calculateGasOf(txData, from, safeAddress) + + const executeTransactionTxData = await safeInstance.methods + .execTransaction( + txRecipient, + txAmount as string, + txData, + operation as number, + safeTxGas as string, + baseGas as number, + gasPrice as string, + gasToken as string, + refundReceiver as string, + sigs, + ) + .encodeABI() + + const gasEstimation = await calculateGasOf(executeTransactionTxData, from, safeAddress) + + const gasBatches = [gasEstimation, 10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000] + .filter((currentGas) => currentGas < gasEstimation) + // Reorders gas from lowest to highest + .sort((a, b) => b - a) + + for (const baseGasIterator of gasBatches) { + const executeTransactionGasCheck = await safeInstance.methods + .execTransaction( + txRecipient, + txAmount as string, + txData, + operation as number, + safeTxGas as string, + gasEstimation, + gasPrice as string, + gasToken as string, + refundReceiver as string, + sigs, + ) + .call() + + if (executeTransactionGasCheck) { + return baseGasIterator + } + } + + // In there is no gasBatches available that could run successfully execTransaction we need to inform the user + throw new Error('There was no valid value of gas to execute the transaction, the transaction may fail') +} + +type TransactionApprovalEstimationProps = { + txData: string + safeAddress: string + txRecipient: string + txAmount?: string + operation?: number + from: string + isOffChainSignature: boolean +} + +export const estimateGasForTransactionApproval = async ({ + safeAddress, + txRecipient, + txAmount, + txData, + operation, + from, + isOffChainSignature, +}: TransactionApprovalEstimationProps): Promise => { + if (isOffChainSignature) { + return 0 + } + + const safeInstance = await getGnosisSafeInstanceAt(safeAddress) + + const nonce = await safeInstance.methods.nonce().call() + const txHash = await safeInstance.methods + .getTransactionHash( + txRecipient, + txAmount || '0', + txData, + operation as number, + 0, + 0, + 0, + ZERO_ADDRESS, + ZERO_ADDRESS, + nonce, + ) + .call({ + from, + }) + const approveTransactionTxData = await safeInstance.methods.approveHash(txHash).encodeABI() + return calculateGasOf(approveTransactionTxData, from, safeAddress) +} From 25bbd331be00beb9a99772f308fa7fdbd6ee32b5 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Tue, 5 Jan 2021 14:17:49 -0300 Subject: [PATCH 06/25] Move estimateTransactionGas to useEstimateTransactionGas Refactors useEstimateTransactionGas to return isCreation and isOffChainSignature --- src/logic/hooks/useEstimateTransactionGas.tsx | 117 +++++++++++++++++- 1 file changed, 111 insertions(+), 6 deletions(-) diff --git a/src/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx index e6f8f6eac7..7c669b0fed 100644 --- a/src/logic/hooks/useEstimateTransactionGas.tsx +++ b/src/logic/hooks/useEstimateTransactionGas.tsx @@ -1,11 +1,22 @@ import { useEffect, useState } from 'react' -import { estimateTransactionGas } from 'src/logic/safe/transactions/gas' +import { + estimateGasForTransactionApproval, + estimateGasForTransactionCreation, + estimateGasForTransactionExecution, +} from 'src/logic/safe/transactions/gas' import { fromTokenUnit } from 'src/logic/tokens/utils/humanReadableValue' import { formatAmount } from 'src/logic/tokens/utils/formatAmount' import { calculateGasPrice } from 'src/logic/wallets/ethTransactions' import { getNetworkInfo } from 'src/config' import { useSelector } from 'react-redux' -import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' +import { + safeCurrentVersionSelector, + safeParamAddressFromStateSelector, + safeThresholdSelector, +} from 'src/logic/safe/store/selectors' +import { CALL, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES } from '../safe/transactions' +import semverSatisfies from 'semver/functions/satisfies' +import { providerSelector } from '../wallets/store/selectors' export enum EstimationStatus { LOADING = 'LOADING', @@ -16,6 +27,64 @@ export enum EstimationStatus { const checkIfTxIsExecution = (threshold: number, preApprovingOwner?: string, txConfirmations?: number): boolean => txConfirmations === threshold || !!preApprovingOwner || threshold === 1 +const checkIfTxIsCreation = (txConfirmations: number): boolean => txConfirmations === 0 + +const checkIfOffChainSignatureIsPossible = ( + isExecution: boolean, + isSmartContractWallet: boolean, + safeVersion?: string, +): boolean => + !isExecution && + !isSmartContractWallet && + !!safeVersion && + semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) + +type TransactionEstimationProps = { + txData: string + safeAddress: string + txRecipient: string + isExecution: boolean + isCreation: boolean + isOffChainSignature?: boolean + txAmount?: string + operation?: number + from?: string +} + +const estimateTransactionGas = async ({ + txAmount, + txData, + txRecipient, + isExecution, + isCreation, + isOffChainSignature = false, + safeAddress, + operation = CALL, + from, +}: TransactionEstimationProps): Promise => { + if (isCreation) { + return estimateGasForTransactionCreation(safeAddress, txData, txRecipient, txAmount || '0', operation) + } + + if (!from) { + throw new Error('No from provided for approving or execute transaction') + } + + if (isExecution) { + return estimateGasForTransactionExecution({ safeAddress, txRecipient, txAmount, txData, operation, from }) + } + + return estimateGasForTransactionApproval({ + safeAddress, + operation, + txData, + txAmount, + txRecipient, + from, + isOffChainSignature, + }) +} + type UseEstimateTransactionGasProps = { txData: string txRecipient: string @@ -32,6 +101,8 @@ type TransactionGasEstimationResult = { gasCostFormatted: string // Cost of gas in format '< | > 100' gasPrice: string // Current price of gas unit isExecution: boolean // Returns true if the user will execute the tx or false if it just signs it + isCreation: boolean // Returns true if the transaction is a creation transaction + isOffChainSignature: boolean // Returns true if offChainSignature is available } export const useEstimateTransactionGas = ({ @@ -49,10 +120,14 @@ export const useEstimateTransactionGas = ({ gasCostFormatted: '< 0.001', gasPrice: '0', isExecution: false, + isCreation: false, + isOffChainSignature: false, }) const { nativeCoin } = getNetworkInfo() const safeAddress = useSelector(safeParamAddressFromStateSelector) const threshold = useSelector(safeThresholdSelector) + const safeVersion = useSelector(safeCurrentVersionSelector) + const { account: from, smartContractWallet } = useSelector(providerSelector) useEffect(() => { let isCurrent = true @@ -62,8 +137,11 @@ export const useEstimateTransactionGas = ({ return } + const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations) + const isCreation = checkIfTxIsCreation(txConfirmations || 0) + try { - const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations) + const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion) const gasEstimation = await estimateTransactionGas({ safeAddress, @@ -71,20 +149,32 @@ export const useEstimateTransactionGas = ({ txData, txAmount, isExecution, + isCreation, + isOffChainSignature, operation, + from, }) const gasPrice = await calculateGasPrice() const estimatedGasCosts = gasEstimation * parseInt(gasPrice, 10) const gasCost = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals) const gasCostFormatted = formatAmount(gasCost) + if (isCurrent) { + let txEstimationExecutionStatus = EstimationStatus.SUCCESS + + if (gasEstimation <= 0) { + txEstimationExecutionStatus = isOffChainSignature ? EstimationStatus.SUCCESS : EstimationStatus.FAILURE + } + setGasEstimation({ - txEstimationExecutionStatus: gasEstimation <= 0 ? EstimationStatus.FAILURE : EstimationStatus.SUCCESS, + txEstimationExecutionStatus, gasEstimation, gasCost, gasCostFormatted, gasPrice, isExecution, + isCreation, + isOffChainSignature, }) } } catch (error) { @@ -98,7 +188,9 @@ export const useEstimateTransactionGas = ({ gasCost, gasCostFormatted, gasPrice: '1', - isExecution: false, + isExecution, + isCreation, + isOffChainSignature: false, }) } } @@ -108,7 +200,20 @@ export const useEstimateTransactionGas = ({ return () => { isCurrent = false } - }, [txData, safeAddress, txRecipient, txConfirmations, txAmount, preApprovingOwner, nativeCoin.decimals, threshold]) + }, [ + txData, + safeAddress, + txRecipient, + txConfirmations, + txAmount, + preApprovingOwner, + nativeCoin.decimals, + threshold, + from, + operation, + safeVersion, + smartContractWallet, + ]) return gasEstimation } From 789592471b7fc930367a1f3d66cee546fca465c0 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 11:45:01 -0300 Subject: [PATCH 07/25] Type and refactor generateSignaturesFromTxConfirmations Moves getPreValidatedSignatures to safeTxSigner.ts --- src/logic/safe/safeTxSigner.ts | 61 +++++++++++-------- .../safe/store/actions/createTransaction.ts | 3 +- .../safe/store/actions/processTransaction.ts | 6 +- src/test/signatures.blockchain.ts | 10 ++- 4 files changed, 50 insertions(+), 30 deletions(-) diff --git a/src/logic/safe/safeTxSigner.ts b/src/logic/safe/safeTxSigner.ts index 52de1a3b68..811a7c36f5 100644 --- a/src/logic/safe/safeTxSigner.ts +++ b/src/logic/safe/safeTxSigner.ts @@ -1,31 +1,44 @@ -// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures -// https://github.com/gnosis/safe-contracts/blob/master/test/gnosisSafeTeamEdition.js#L26 -export const generateSignaturesFromTxConfirmations = (confirmations, preApprovingOwner) => { - // The constant parts need to be sorted so that the recovered signers are sorted ascending - // (natural order) by address (not checksummed). - const confirmationsMap = confirmations.reduce((map, obj) => { - map[obj.owner.toLowerCase()] = obj // eslint-disable-line no-param-reassign - return map - }, {}) +import { List } from 'immutable' +import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' +import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' + +// https://docs.gnosis.io/safe/docs/contracts_signatures/#pre-validated-signatures +export const getPreValidatedSignatures = (from: string, initialString: string = EMPTY_DATA): string => { + return `${initialString}000000000000000000000000${from.replace( + EMPTY_DATA, + '', + )}000000000000000000000000000000000000000000000000000000000000000001` +} + +export const generateSignaturesFromTxConfirmations = ( + confirmations?: List, + preApprovingOwner?: string, +): string => { + let confirmationsMap = + confirmations?.map((value) => { + return { + signature: value.signature, + owner: value.owner.toLowerCase(), + } + }) || List([]) if (preApprovingOwner) { - confirmationsMap[preApprovingOwner.toLowerCase()] = { owner: preApprovingOwner } + confirmationsMap.push({ owner: preApprovingOwner, signature: null }) } + // The constant parts need to be sorted so that the recovered signers are sorted ascending + // (natural order) by address (not checksummed). + confirmationsMap = confirmationsMap.sort((ownerA, ownerB) => ownerA.owner.localeCompare(ownerB.owner)) + let sigs = '0x' - Object.keys(confirmationsMap) - .sort() - .forEach((addr) => { - const conf = confirmationsMap[addr] - if (conf.signature) { - sigs += conf.signature.slice(2) - } else { - // https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures - sigs += `000000000000000000000000${addr.replace( - '0x', - '', - )}000000000000000000000000000000000000000000000000000000000000000001` - } - }) + confirmationsMap.forEach(({ signature, owner }) => { + if (signature) { + sigs += signature.slice(2) + } else { + // https://docs.gnosis.io/safe/docs/contracts_signatures/#pre-validated-signatures + sigs += getPreValidatedSignatures(owner, '') + } + }) + return sigs } diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index 97969aec5f..efa6fa3002 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -14,7 +14,7 @@ import { saveTxToHistory, tryOffchainSigning, } from 'src/logic/safe/transactions' -import { estimateGasForTransactionCreation, getPreValidatedSignatures } from 'src/logic/safe/transactions/gas' +import { estimateGasForTransactionCreation } from 'src/logic/safe/transactions/gas' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' @@ -40,6 +40,7 @@ import { AnyAction } from 'redux' import { PayableTx } from 'src/types/contracts/types.d' import { AppReduxState } from 'src/store' import { Dispatch, DispatchReturn } from './types' +import { getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner' export interface CreateTransactionArgs { navigateToTransactionsTab?: boolean diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts index b08c966cf4..2b91c7eaa7 100644 --- a/src/logic/safe/store/actions/processTransaction.ts +++ b/src/logic/safe/store/actions/processTransaction.ts @@ -4,7 +4,7 @@ import semverSatisfies from 'semver/functions/satisfies' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getNotificationsFromTxType } from 'src/logic/notifications' -import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner' +import { generateSignaturesFromTxConfirmations, getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner' import { getApprovalTransaction, getExecutionTransaction, saveTxToHistory } from 'src/logic/safe/transactions' import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/logic/safe/transactions/offchainSigner' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' @@ -22,7 +22,6 @@ import { storeExecutedTx, storeSignedTx, storeTx } from 'src/logic/safe/store/ac import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { Dispatch, DispatchReturn } from './types' -import { getPreValidatedSignatures } from 'src/logic/safe/transactions/gas' interface ProcessTransactionArgs { approveAndExecute: boolean @@ -54,7 +53,8 @@ const processTransaction = ({ const isExecution = approveAndExecute || (await shouldExecuteTransaction(safeInstance, nonce, lastTx)) const safeVersion = await getCurrentSafeVersion(safeInstance) - let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, approveAndExecute && userAddress) + const preApprovingOwner = approveAndExecute ? userAddress : undefined + let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner) if (!sigs) { sigs = getPreValidatedSignatures(from) diff --git a/src/test/signatures.blockchain.ts b/src/test/signatures.blockchain.ts index 86b983df68..1182eea173 100644 --- a/src/test/signatures.blockchain.ts +++ b/src/test/signatures.blockchain.ts @@ -1,8 +1,14 @@ -// +// import { List } from 'immutable' import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner' +import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' +import { makeConfirmation } from 'src/logic/safe/store/models/confirmation' -const makeMockConfirmation = (address) => ({ owner: { address } }) +const makeMockConfirmation = (address: string): Confirmation => { + return makeConfirmation({ + owner: address + }) +} describe('Signatures Blockchain Test', () => { it('generates signatures in natural order even checksumed', async () => { From d8d64c9aec97c792500c96464080fe326857b9bb Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 11:49:46 -0300 Subject: [PATCH 08/25] Uses confirmations to estimateGasForTransactionExecution --- src/logic/hooks/useEstimateTransactionGas.tsx | 22 +++++++++++++++---- src/logic/safe/transactions/gas.ts | 21 ++++++++---------- 2 files changed, 27 insertions(+), 16 deletions(-) diff --git a/src/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx index 7c669b0fed..287643d1e8 100644 --- a/src/logic/hooks/useEstimateTransactionGas.tsx +++ b/src/logic/hooks/useEstimateTransactionGas.tsx @@ -18,6 +18,9 @@ import { CALL, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES } from '../safe/transactions import semverSatisfies from 'semver/functions/satisfies' import { providerSelector } from '../wallets/store/selectors' +import { List } from 'immutable' +import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' + export enum EstimationStatus { LOADING = 'LOADING', FAILURE = 'FAILURE', @@ -43,6 +46,7 @@ type TransactionEstimationProps = { txData: string safeAddress: string txRecipient: string + txConfirmations?: List isExecution: boolean isCreation: boolean isOffChainSignature?: boolean @@ -55,6 +59,7 @@ const estimateTransactionGas = async ({ txAmount, txData, txRecipient, + txConfirmations, isExecution, isCreation, isOffChainSignature = false, @@ -71,7 +76,15 @@ const estimateTransactionGas = async ({ } if (isExecution) { - return estimateGasForTransactionExecution({ safeAddress, txRecipient, txAmount, txData, operation, from }) + return estimateGasForTransactionExecution({ + safeAddress, + txRecipient, + txConfirmations, + txAmount, + txData, + operation, + from, + }) } return estimateGasForTransactionApproval({ @@ -88,7 +101,7 @@ const estimateTransactionGas = async ({ type UseEstimateTransactionGasProps = { txData: string txRecipient: string - txConfirmations?: number + txConfirmations?: List txAmount?: string preApprovingOwner?: string operation?: number @@ -137,8 +150,8 @@ export const useEstimateTransactionGas = ({ return } - const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations) - const isCreation = checkIfTxIsCreation(txConfirmations || 0) + const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size) + const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0) try { const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion) @@ -148,6 +161,7 @@ export const useEstimateTransactionGas = ({ txRecipient, txData, txAmount, + txConfirmations, isExecution, isCreation, isOffChainSignature, diff --git a/src/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts index d05b270ed9..ff84d2fd29 100644 --- a/src/logic/safe/transactions/gas.ts +++ b/src/logic/safe/transactions/gas.ts @@ -5,6 +5,9 @@ import { getWeb3 } from 'src/logic/wallets/getWeb3' import { sameString } from 'src/utils/strings' import { CALL } from './send' import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses' +import { generateSignaturesFromTxConfirmations } from 'src/logic/safe/safeTxSigner' +import { List } from 'immutable' +import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' // Receives the response data of the safe method requiredTxGas() and parses it to get the gas amount const parseRequiredTxGasResponse = (data: string): number => { @@ -23,14 +26,6 @@ const parseRequiredTxGasResponse = (data: string): number => { return data.match(/.{2}/g)?.reduce(reducer, 0) } -// https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures -export const getPreValidatedSignatures = (from: string): string => { - return `0x000000000000000000000000${from.replace( - EMPTY_DATA, - '', - )}000000000000000000000000000000000000000000000000000000000000000001` -} - // Parses the result from the error message (GETH, OpenEthereum/Parity and Nethermind) and returns the data value export const getDataFromNodeErrorMessage = (errorMessage: string): string | undefined => { // Replace illegal characters that often comes within the error string (like � for example) @@ -173,6 +168,7 @@ type TransactionExecutionEstimationProps = { txData: string safeAddress: string txRecipient: string + txConfirmations?: List txAmount?: string operation?: number gasPrice?: string @@ -185,6 +181,7 @@ type TransactionExecutionEstimationProps = { export const estimateGasForTransactionExecution = async ({ safeAddress, txRecipient, + txConfirmations, txAmount = '0', txData, operation = CALL, @@ -195,8 +192,8 @@ export const estimateGasForTransactionExecution = async ({ safeTxGas = '0', }: TransactionExecutionEstimationProps): Promise => { const safeInstance = await getGnosisSafeInstanceAt(safeAddress) - const sigs = getPreValidatedSignatures(from) - const baseGas = await calculateGasOf(txData, from, safeAddress) + + const sigs = generateSignaturesFromTxConfirmations(txConfirmations) const executeTransactionTxData = await safeInstance.methods .execTransaction( @@ -205,7 +202,7 @@ export const estimateGasForTransactionExecution = async ({ txData, operation as number, safeTxGas as string, - baseGas as number, + 0, gasPrice as string, gasToken as string, refundReceiver as string, @@ -215,7 +212,7 @@ export const estimateGasForTransactionExecution = async ({ const gasEstimation = await calculateGasOf(executeTransactionTxData, from, safeAddress) - const gasBatches = [gasEstimation, 10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000] + const gasBatches = [0, 10000, 20000, 40000, 80000, 160000, 320000, 640000, 1280000, 2560000, 5120000] .filter((currentGas) => currentGas < gasEstimation) // Reorders gas from lowest to highest .sort((a, b) => b - a) From 88d36f8ff45b0fd367c37773118b121e7658f7a3 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 11:52:11 -0300 Subject: [PATCH 09/25] Adds TransactionFeesText component Uses TransactionFeesText on ApproveTxModal --- src/components/TransactionsFeesText/index.tsx | 38 +++++++++++++++++++ .../ExpandedTx/ApproveTxModal/index.tsx | 29 +++++++------- 2 files changed, 54 insertions(+), 13 deletions(-) create mode 100644 src/components/TransactionsFeesText/index.tsx diff --git a/src/components/TransactionsFeesText/index.tsx b/src/components/TransactionsFeesText/index.tsx new file mode 100644 index 0000000000..d3080dd646 --- /dev/null +++ b/src/components/TransactionsFeesText/index.tsx @@ -0,0 +1,38 @@ +import React from 'react' +import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas' +import Paragraph from 'src/components/layout/Paragraph' +import { getNetworkInfo } from 'src/config' + +type TransactionFailTextProps = { + txEstimationExecutionStatus: EstimationStatus + gasCostFormatted: string + isExecution: boolean + isCreation: boolean + isOffChainSignature: boolean +} +const { nativeCoin } = getNetworkInfo() + +export const TransactionFeesText = ({ + gasCostFormatted, + isExecution, + isCreation, + isOffChainSignature, +}: TransactionFailTextProps): React.ReactElement | null => { + let transactionAction + if (isCreation) { + transactionAction = 'create' + } else if (isExecution) { + transactionAction = 'execute' + } else { + transactionAction = 'approve' + } + + return ( + + You're about to {transactionAction} a transaction and will have to confirm it with your currently connected + wallet. + {!isOffChainSignature && + ` Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} + + ) +} diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx index 40060bfffd..1f57c56f91 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx @@ -5,7 +5,6 @@ import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import React, { useState } from 'react' import { useDispatch, useSelector } from 'react-redux' -import { getNetworkInfo } from 'src/config' import { styles } from './style' @@ -24,6 +23,7 @@ import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/lo import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFeesText } from 'src/components/TransactionsFeesText' const useStyles = makeStyles(styles) @@ -61,7 +61,6 @@ type Props = { thresholdReached: boolean tx: Transaction } -const { nativeCoin } = getNetworkInfo() export const ApproveTxModal = ({ canExecute, @@ -81,10 +80,16 @@ export const ApproveTxModal = ({ const oneConfirmationLeft = !thresholdReached && tx.confirmations.size + 1 === threshold const isTheTxReadyToBeExecuted = oneConfirmationLeft ? true : thresholdReached - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isOffChainSignature, + isCreation, + } = useEstimateTransactionGas({ txRecipient: tx.recipient, txData: tx.data || '', - txConfirmations: tx.confirmations.size, + txConfirmations: tx.confirmations, txAmount: tx.value, preApprovingOwner: approveAndExecute ? userAddress : undefined, }) @@ -140,15 +145,13 @@ export 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 ${gasCostFormatted} (fee price) ${ - nativeCoin.name - } in this wallet to fund this confirmation.`} - - + From 7c6ba58681d0a64f25467aab9dbfa3a56959fa0f Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 13:01:27 -0300 Subject: [PATCH 10/25] Pass more parameters to estimateGasForTransactionExecution --- src/logic/hooks/useEstimateTransactionGas.tsx | 26 ++++++++++++++----- 1 file changed, 19 insertions(+), 7 deletions(-) diff --git a/src/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx index 287643d1e8..48efd19f30 100644 --- a/src/logic/hooks/useEstimateTransactionGas.tsx +++ b/src/logic/hooks/useEstimateTransactionGas.tsx @@ -47,25 +47,33 @@ type TransactionEstimationProps = { safeAddress: string txRecipient: string txConfirmations?: List - isExecution: boolean - isCreation: boolean - isOffChainSignature?: boolean txAmount?: string operation?: number + gasPrice?: string + gasToken?: string + refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin). + safeTxGas?: string from?: string + isExecution: boolean + isCreation: boolean + isOffChainSignature?: boolean } const estimateTransactionGas = async ({ - txAmount, txData, + safeAddress, txRecipient, txConfirmations, + txAmount, + operation = CALL, + gasPrice, + gasToken, + refundReceiver, + safeTxGas, + from, isExecution, isCreation, isOffChainSignature = false, - safeAddress, - operation = CALL, - from, }: TransactionEstimationProps): Promise => { if (isCreation) { return estimateGasForTransactionCreation(safeAddress, txData, txRecipient, txAmount || '0', operation) @@ -84,6 +92,10 @@ const estimateTransactionGas = async ({ txData, operation, from, + gasPrice, + gasToken, + refundReceiver, + safeTxGas, }) } From c6efa914a7631cf3f2475d9d7eed5c723b31cad1 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 13:33:11 -0300 Subject: [PATCH 11/25] Removes unnecessary parameter in getNewTxNonce --- .../store/actions/__tests__/utils.test.ts | 19 ++----------------- .../safe/store/actions/createTransaction.ts | 2 +- src/logic/safe/store/actions/utils.ts | 10 +--------- .../transactions/__tests__/utils.test.ts | 18 +++--------------- 4 files changed, 7 insertions(+), 42 deletions(-) diff --git a/src/logic/safe/store/actions/__tests__/utils.test.ts b/src/logic/safe/store/actions/__tests__/utils.test.ts index db063f9ace..b11ebcb35f 100644 --- a/src/logic/safe/store/actions/__tests__/utils.test.ts +++ b/src/logic/safe/store/actions/__tests__/utils.test.ts @@ -3,22 +3,8 @@ import { GnosisSafe } from 'src/types/contracts/GnosisSafe.d' import { TxServiceModel } from 'src/logic/safe/store/actions/transactions/fetchTransactions/loadOutgoingTransactions' describe('Store actions utils > getNewTxNonce', () => { - it(`Should return passed predicted transaction nonce if it's a valid value`, async () => { - // Given - const txNonce = '45' - const lastTx = { nonce: 44 } as TxServiceModel - const safeInstance = {} - - // When - const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance as GnosisSafe) - - // Then - expect(nonce).toBe('45') - }) - it(`Should return nonce of a last transaction + 1 if passed nonce is less than last transaction or invalid`, async () => { // Given - const txNonce = '' const lastTx = { nonce: 44 } as TxServiceModel const safeInstance = { methods: { @@ -29,7 +15,7 @@ describe('Store actions utils > getNewTxNonce', () => { } // When - const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance as GnosisSafe) + const nonce = await getNewTxNonce(lastTx, safeInstance as GnosisSafe) // Then expect(nonce).toBe('45') @@ -37,7 +23,6 @@ describe('Store actions utils > getNewTxNonce', () => { it(`Should retrieve contract's instance nonce value as a fallback, if txNonce and lastTx are not valid`, async () => { // Given - const txNonce = '' const lastTx = null const safeInstance = { methods: { @@ -48,7 +33,7 @@ describe('Store actions utils > getNewTxNonce', () => { } // When - const nonce = await getNewTxNonce(txNonce, lastTx, safeInstance as GnosisSafe) + const nonce = await getNewTxNonce(lastTx, safeInstance as GnosisSafe) // Then expect(nonce).toBe('45') diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index efa6fa3002..f62e56855a 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -88,7 +88,7 @@ const createTransaction = ( const { account: from, hardwareWallet, smartContractWallet } = providerSelector(state) const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const lastTx = await getLastTx(safeAddress) - const nonce = await getNewTxNonce(txNonce?.toString(), lastTx, safeInstance) + const nonce = txNonce ? txNonce.toString() : await getNewTxNonce(lastTx, safeInstance) const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx) const safeVersion = await getCurrentSafeVersion(safeInstance) let safeTxGas diff --git a/src/logic/safe/store/actions/utils.ts b/src/logic/safe/store/actions/utils.ts index 74b59b0d3c..828c6e92e6 100644 --- a/src/logic/safe/store/actions/utils.ts +++ b/src/logic/safe/store/actions/utils.ts @@ -16,15 +16,7 @@ export const getLastTx = async (safeAddress: string): Promise => { - if (txNonce) { - return txNonce - } - +export const getNewTxNonce = async (lastTx: TxServiceModel | null, safeInstance: GnosisSafe): Promise => { // use current's safe nonce as fallback return lastTx ? `${lastTx.nonce + 1}` : (await safeInstance.methods.nonce().call()).toString() } diff --git a/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts b/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts index db0b015c06..c10615b213 100644 --- a/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts +++ b/src/routes/safe/store/actions/transactions/__tests__/utils.test.ts @@ -66,7 +66,7 @@ describe('getNewTxNonce', () => { const expectedResult = '2' // when - const result = await getNewTxNonce(undefined, lastTx, safeInstance) + const result = await getNewTxNonce(lastTx, safeInstance) // then expect(result).toBe(expectedResult) @@ -82,7 +82,7 @@ describe('getNewTxNonce', () => { safeInstance.methods.nonce = mockFnNonce // when - const result = await getNewTxNonce(undefined, null, safeInstance) + const result = await getNewTxNonce(null, safeInstance) // then expect(result).toBe(expectedResult) @@ -98,19 +98,7 @@ describe('getNewTxNonce', () => { const lastTx = getMockedTxServiceModel({ nonce: 10 }) // when - const result = await getNewTxNonce(undefined, lastTx, safeInstance) - - // then - expect(result).toBe(expectedResult) - }) - it('Given a pre-calculated nonce number should return it', async () => { - // given - const safeInstance = getMockedSafeInstance({}) - const expectedResult = '114' - const nextNonce = '114' - - // when - const result = await getNewTxNonce(nextNonce, null, safeInstance) + const result = await getNewTxNonce(lastTx, safeInstance) // then expect(result).toBe(expectedResult) From f9fb7069b52f9bb391498e0da196470138ca010f Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 13:35:21 -0300 Subject: [PATCH 12/25] Moves checkIfOffChainSignatureIsPossible to safeTxSigner.ts --- src/logic/hooks/useEstimateTransactionGas.tsx | 14 ++--------- src/logic/safe/safeTxSigner.ts | 17 +++++++++++++ .../safe/store/actions/processTransaction.ts | 24 +++++++------------ .../ExpandedTx/ApproveTxModal/index.tsx | 2 +- 4 files changed, 29 insertions(+), 28 deletions(-) diff --git a/src/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx index 6431c4b37e..17d3797060 100644 --- a/src/logic/hooks/useEstimateTransactionGas.tsx +++ b/src/logic/hooks/useEstimateTransactionGas.tsx @@ -14,12 +14,12 @@ import { safeParamAddressFromStateSelector, safeThresholdSelector, } from 'src/logic/safe/store/selectors' -import { CALL, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES } from '../safe/transactions' -import semverSatisfies from 'semver/functions/satisfies' +import { CALL } from 'src/logic/safe/transactions' import { providerSelector } from '../wallets/store/selectors' import { List } from 'immutable' import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' +import { checkIfOffChainSignatureIsPossible } from 'src/logic/safe/safeTxSigner' export enum EstimationStatus { LOADING = 'LOADING', @@ -32,16 +32,6 @@ const checkIfTxIsExecution = (threshold: number, preApprovingOwner?: string, txC const checkIfTxIsCreation = (txConfirmations: number): boolean => txConfirmations === 0 -const checkIfOffChainSignatureIsPossible = ( - isExecution: boolean, - isSmartContractWallet: boolean, - safeVersion?: string, -): boolean => - !isExecution && - !isSmartContractWallet && - !!safeVersion && - semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) - type TransactionEstimationProps = { txData: string safeAddress: string diff --git a/src/logic/safe/safeTxSigner.ts b/src/logic/safe/safeTxSigner.ts index 811a7c36f5..df93ad422e 100644 --- a/src/logic/safe/safeTxSigner.ts +++ b/src/logic/safe/safeTxSigner.ts @@ -1,6 +1,23 @@ import { List } from 'immutable' import { Confirmation } from 'src/logic/safe/store/models/types/confirmation' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' +import semverSatisfies from 'semver/functions/satisfies' +import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES } from './transactions/offchainSigner' + +// Here we're checking that safe contract version is greater or equal 1.1.1, but +// theoretically EIP712 should also work for 1.0.0 contracts +// Also, offchain signatures are not working for ledger/trezor wallet because of a bug in their library: +// https://github.com/LedgerHQ/ledgerjs/issues/378 +// Couldn't find an issue for trezor but the error is almost the same +export const checkIfOffChainSignatureIsPossible = ( + isExecution: boolean, + isSmartContractWallet: boolean, + safeVersion?: string, +): boolean => + !isExecution && + !isSmartContractWallet && + !!safeVersion && + semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) // https://docs.gnosis.io/safe/docs/contracts_signatures/#pre-validated-signatures export const getPreValidatedSignatures = (from: string, initialString: string = EMPTY_DATA): string => { diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts index 2b91c7eaa7..41fc17cfd9 100644 --- a/src/logic/safe/store/actions/processTransaction.ts +++ b/src/logic/safe/store/actions/processTransaction.ts @@ -1,12 +1,15 @@ import { AnyAction } from 'redux' import { ThunkAction } from 'redux-thunk' -import semverSatisfies from 'semver/functions/satisfies' import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts' import { getNotificationsFromTxType } from 'src/logic/notifications' -import { generateSignaturesFromTxConfirmations, getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner' +import { + checkIfOffChainSignatureIsPossible, + generateSignaturesFromTxConfirmations, + getPreValidatedSignatures, +} from 'src/logic/safe/safeTxSigner' import { getApprovalTransaction, getExecutionTransaction, saveTxToHistory } from 'src/logic/safe/transactions' -import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/logic/safe/transactions/offchainSigner' +import { tryOffchainSigning } from 'src/logic/safe/transactions/offchainSigner' import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion' import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions' import { providerSelector } from 'src/logic/wallets/store/selectors' @@ -33,7 +36,7 @@ interface ProcessTransactionArgs { type ProcessTransactionAction = ThunkAction, AppReduxState, DispatchReturn, AnyAction> -const processTransaction = ({ +export const processTransaction = ({ approveAndExecute, notifiedTransaction, safeAddress, @@ -49,7 +52,7 @@ const processTransaction = ({ const safeInstance = await getGnosisSafeInstanceAt(safeAddress) const lastTx = await getLastTx(safeAddress) - const nonce = await getNewTxNonce(undefined, lastTx, safeInstance) + const nonce = await getNewTxNonce(lastTx, safeInstance) const isExecution = approveAndExecute || (await shouldExecuteTransaction(safeInstance, nonce, lastTx)) const safeVersion = await getCurrentSafeVersion(safeInstance) @@ -84,14 +87,7 @@ const processTransaction = ({ } try { - // Here we're checking that safe contract version is greater or equal 1.1.1, but - // theoretically EIP712 should also work for 1.0.0 contracts - // Also, offchain signatures are not working for ledger/trezor wallet because of a bug in their library: - // https://github.com/LedgerHQ/ledgerjs/issues/378 - // Couldn't find an issue for trezor but the error is almost the same - const canTryOffchainSigning = - !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) - if (canTryOffchainSigning) { + if (checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion)) { const signature = await tryOffchainSigning(tx.safeTxHash, { ...txArgs, safeAddress }, hardwareWallet) if (signature) { @@ -194,5 +190,3 @@ const processTransaction = ({ return txHash } - -export default processTransaction diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx index 1f57c56f91..670c689594 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx @@ -17,7 +17,7 @@ import Paragraph from 'src/components/layout/Paragraph' import Row from 'src/components/layout/Row' import { TX_NOTIFICATION_TYPES } from 'src/logic/safe/transactions' import { userAccountSelector } from 'src/logic/wallets/store/selectors' -import processTransaction from 'src/logic/safe/store/actions/processTransaction' +import { processTransaction } from 'src/logic/safe/store/actions/processTransaction' import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' import { Transaction } from 'src/logic/safe/store/models/types/transaction' From e0b3af66f854957863ad5a767a8db20833542a33 Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 14:58:09 -0300 Subject: [PATCH 13/25] Fix check for null confirmations --- .../Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx index 35ae624fad..c23827af6d 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/OwnersColumn/index.tsx @@ -33,7 +33,7 @@ function getOwnersConfirmations(tx: Transaction, userAddress: string): [string[] const ownersWhoConfirmed: string[] = [] let currentUserAlreadyConfirmed = false - tx.confirmations.forEach((conf) => { + tx.confirmations?.forEach((conf) => { if (conf.owner === userAddress) { currentUserAlreadyConfirmed = true } From d819531013a0e9ac29354a5727ac15b04b3ae25f Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 14:58:59 -0300 Subject: [PATCH 14/25] Uses checkIfOffChainSignatureIsPossible on createTransaction.ts --- src/logic/safe/store/actions/createTransaction.ts | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/src/logic/safe/store/actions/createTransaction.ts b/src/logic/safe/store/actions/createTransaction.ts index f62e56855a..3561dc56ab 100644 --- a/src/logic/safe/store/actions/createTransaction.ts +++ b/src/logic/safe/store/actions/createTransaction.ts @@ -1,5 +1,4 @@ import { push } from 'connected-react-router' -import semverSatisfies from 'semver/functions/satisfies' import { ThunkAction } from 'redux-thunk' import { onboardUser } from 'src/components/ConnectButton' @@ -10,7 +9,6 @@ import { CALL, getApprovalTransaction, getExecutionTransaction, - SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, saveTxToHistory, tryOffchainSigning, } from 'src/logic/safe/transactions' @@ -40,7 +38,7 @@ import { AnyAction } from 'redux' import { PayableTx } from 'src/types/contracts/types.d' import { AppReduxState } from 'src/store' import { Dispatch, DispatchReturn } from './types' -import { getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner' +import { checkIfOffChainSignatureIsPossible, getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner' export interface CreateTransactionArgs { navigateToTransactionsTab?: boolean @@ -100,7 +98,6 @@ const createTransaction = ( } const sigs = getPreValidatedSignatures(from) - const notificationsQueue = getNotificationsFromTxType(notifiedTransaction, origin) const beforeExecutionKey = dispatch(enqueueSnackbar(notificationsQueue.beforeExecution)) @@ -125,11 +122,7 @@ const createTransaction = ( const safeTxHash = generateSafeTxHash(safeAddress, txArgs) try { - // Here we're checking that safe contract version is greater or equal 1.1.1, but - // theoretically EIP712 should also work for 1.0.0 contracts - const canTryOffchainSigning = - !isExecution && !smartContractWallet && semverSatisfies(safeVersion, SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES) - if (canTryOffchainSigning) { + if (checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion)) { const signature = await tryOffchainSigning(safeTxHash, { ...txArgs, safeAddress }, hardwareWallet) if (signature) { From 4ab94acc7807dbe47e5e51d8e3a84990a4387eee Mon Sep 17 00:00:00 2001 From: Agustin Pane Date: Wed, 6 Jan 2021 16:20:19 -0300 Subject: [PATCH 15/25] Move TransactionFailText inside TransactionFees component --- .../index.tsx | 19 ++++++++----- .../ContractInteraction/Review/index.tsx | 22 ++++++++++----- .../ReviewCustomTx/index.tsx | 21 ++++++++++----- .../screens/ReviewCollectible/index.tsx | 25 ++++++++++------- .../SendModal/screens/ReviewTx/index.tsx | 21 ++++++++++----- .../AddOwnerModal/screens/Review/index.tsx | 27 +++++++++++-------- .../RemoveOwnerModal/screens/Review/index.tsx | 27 +++++++++++-------- .../screens/Review/index.tsx | 27 +++++++++++-------- .../ChangeThreshold/index.tsx | 24 ++++++++++------- .../ExpandedTx/ApproveTxModal/index.tsx | 6 ++--- .../ExpandedTx/RejectTxModal/index.tsx | 25 ++++++++++------- 11 files changed, 154 insertions(+), 90 deletions(-) rename src/components/{TransactionsFeesText => TransactionsFees}/index.tsx (56%) diff --git a/src/components/TransactionsFeesText/index.tsx b/src/components/TransactionsFees/index.tsx similarity index 56% rename from src/components/TransactionsFeesText/index.tsx rename to src/components/TransactionsFees/index.tsx index d3080dd646..c5e54f1970 100644 --- a/src/components/TransactionsFeesText/index.tsx +++ b/src/components/TransactionsFees/index.tsx @@ -2,6 +2,7 @@ import React from 'react' import { EstimationStatus } from 'src/logic/hooks/useEstimateTransactionGas' import Paragraph from 'src/components/layout/Paragraph' import { getNetworkInfo } from 'src/config' +import { TransactionFailText } from 'src/components/TransactionFailText' type TransactionFailTextProps = { txEstimationExecutionStatus: EstimationStatus @@ -12,11 +13,12 @@ type TransactionFailTextProps = { } const { nativeCoin } = getNetworkInfo() -export const TransactionFeesText = ({ +export const TransactionFees = ({ gasCostFormatted, isExecution, isCreation, isOffChainSignature, + txEstimationExecutionStatus, }: TransactionFailTextProps): React.ReactElement | null => { let transactionAction if (isCreation) { @@ -28,11 +30,14 @@ export const TransactionFeesText = ({ } return ( - - You're about to {transactionAction} a transaction and will have to confirm it with your currently connected - wallet. - {!isOffChainSignature && - ` Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - + <> + + You're about to {transactionAction} a transaction and will have to confirm it with your currently connected + wallet. + {!isOffChainSignature && + ` Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} + + + ) } diff --git a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx index 4199510a3b..7309b0ef73 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/Review/index.tsx @@ -22,7 +22,7 @@ import createTransaction from 'src/logic/safe/store/actions/createTransaction' import { safeParamAddressFromStateSelector } from 'src/logic/safe/store/selectors' import { generateFormFieldKey, getValueFromTxInputs } from '../utils' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' const useStyles = makeStyles(styles) @@ -52,7 +52,13 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE txAmount: string }>({ txData: '', txAmount: '', txRecipient: '' }) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isOffChainSignature, + isCreation, + } = useEstimateTransactionGas({ txRecipient: txParameters?.txRecipient, txAmount: txParameters?.txAmount, txData: txParameters?.txData, @@ -82,6 +88,7 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE } onClose() } + console.log('ewview') return ( <> @@ -156,10 +163,13 @@ const ContractInteractionReview = ({ onClose, onPrev, tx }: Props): React.ReactE - - {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - - + diff --git a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/ReviewCustomTx/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/ReviewCustomTx/index.tsx index c8a92d7c3b..cda1aead48 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/ReviewCustomTx/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ContractInteraction/ReviewCustomTx/index.tsx @@ -28,7 +28,7 @@ import ArrowDown from '../../assets/arrow-down.svg' import { styles } from './style' import { ExplorerButton } from '@gnosis.pm/safe-react-components' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' export type CustomTx = { contractAddress?: string @@ -51,7 +51,13 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => { const dispatch = useDispatch() const safeAddress = useSelector(safeParamAddressFromStateSelector) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isCreation, + isOffChainSignature, + } = useEstimateTransactionGas({ txRecipient: tx.contractAddress as string, txData: tx.data ? tx.data.trim() : '', txAmount: tx.value ? toTokenUnit(tx.value, nativeCoin.decimals) : '0', @@ -145,10 +151,13 @@ const ReviewCustomTx = ({ onClose, onPrev, tx }: Props): React.ReactElement => { - - {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - - + diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx index 68c49da891..63d4c942de 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewCollectible/index.tsx @@ -3,7 +3,7 @@ import { useDispatch, useSelector } from 'react-redux' import IconButton from '@material-ui/core/IconButton' import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' -import { getExplorerInfo, getNetworkInfo } from 'src/config' +import { getExplorerInfo } from 'src/config' import CopyBtn from 'src/components/CopyBtn' import Identicon from 'src/components/Identicon' import Block from 'src/components/layout/Block' @@ -28,9 +28,7 @@ import ArrowDown from '../assets/arrow-down.svg' import { styles } from './style' import { ExplorerButton } from '@gnosis.pm/safe-react-components' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' - -const { nativeCoin } = getNetworkInfo() +import { TransactionFees } from 'src/components/TransactionsFees' const useStyles = makeStyles(styles) @@ -58,7 +56,13 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement = ) const [data, setData] = useState('') - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isOffChainSignature, + isCreation, + } = useEstimateTransactionGas({ txData: data, txRecipient: tx.recipientAddress, }) @@ -160,10 +164,13 @@ const ReviewCollectible = ({ onClose, onPrev, tx }: Props): React.ReactElement = )} - - {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - - + diff --git a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.tsx b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.tsx index af1cd966a7..8dbb6b3cf2 100644 --- a/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.tsx +++ b/src/routes/safe/components/Balances/SendModal/screens/ReviewTx/index.tsx @@ -36,7 +36,7 @@ import { ExplorerButton } from '@gnosis.pm/safe-react-components' import { TokenProps } from 'src/logic/tokens/store/model/token' import { RecordOf } from 'immutable' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' const useStyles = makeStyles(styles) const { nativeCoin } = getNetworkInfo() @@ -111,7 +111,13 @@ const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement => const txAmount = useTxAmount(tx, isSendingNativeToken, txToken) const data = useTxData(isSendingNativeToken, txAmount, tx.recipientAddress, txToken) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isCreation, + isOffChainSignature, + } = useEstimateTransactionGas({ txData: data, txRecipient, }) @@ -218,10 +224,13 @@ const ReviewTx = ({ onClose, onPrev, tx }: ReviewTxProps): React.ReactElement => - - {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - - + diff --git a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx index 8c1ba1ae73..9329feebaf 100644 --- a/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/AddOwnerModal/screens/Review/index.tsx @@ -4,7 +4,7 @@ import Close from '@material-ui/icons/Close' import classNames from 'classnames' import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' -import { getExplorerInfo, getNetworkInfo } from 'src/config' +import { getExplorerInfo } from 'src/config' import CopyBtn from 'src/components/CopyBtn' import Identicon from 'src/components/Identicon' import Block from 'src/components/layout/Block' @@ -19,12 +19,10 @@ import { safeNameSelector, safeOwnersSelector, safeParamAddressFromStateSelector import { styles } from './style' import { ExplorerButton } from '@gnosis.pm/safe-react-components' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' export const ADD_OWNER_SUBMIT_BTN_TEST_ID = 'add-owner-submit-btn' -const { nativeCoin } = getNetworkInfo() - const useStyles = makeStyles(styles) type ReviewAddOwnerProps = { @@ -45,7 +43,13 @@ export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: Revie const safeName = useSelector(safeNameSelector) const owners = useSelector(safeOwnersSelector) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isOffChainSignature, + isCreation, + } = useEstimateTransactionGas({ txData: data, txRecipient: safeAddress, }) @@ -176,12 +180,13 @@ export const ReviewAddOwner = ({ onClickBack, onClose, onSubmit, values }: Revie - - You're about to create a transaction and will have to confirm it with your currently connected wallet. -
- {`Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} -
- +
diff --git a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx index 3391f3fa09..191e7c89a0 100644 --- a/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/RemoveOwnerModal/screens/Review/index.tsx @@ -4,7 +4,7 @@ import Close from '@material-ui/icons/Close' import classNames from 'classnames' import React, { useEffect, useState } from 'react' import { useSelector } from 'react-redux' -import { getExplorerInfo, getNetworkInfo } from 'src/config' +import { getExplorerInfo } from 'src/config' import CopyBtn from 'src/components/CopyBtn' import Identicon from 'src/components/Identicon' import Block from 'src/components/layout/Block' @@ -22,12 +22,10 @@ import { getOwnersWithNameFromAddressBook } from 'src/logic/addressBook/utils' import { List } from 'immutable' import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' export const REMOVE_OWNER_REVIEW_BTN_TEST_ID = 'remove-owner-review-btn' -const { nativeCoin } = getNetworkInfo() - const useStyles = makeStyles(styles) type ReviewRemoveOwnerProps = { @@ -55,7 +53,13 @@ export const ReviewRemoveOwnerModal = ({ const addressBook = useSelector(addressBookSelector) const ownersWithAddressBookName = owners ? getOwnersWithNameFromAddressBook(addressBook, owners) : List([]) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isCreation, + isOffChainSignature, + } = useEstimateTransactionGas({ txData: data, txRecipient: safeAddress, }) @@ -194,12 +198,13 @@ export const ReviewRemoveOwnerModal = ({ - - You're about to create a transaction and will have to confirm it with your currently connected wallet. -
- {`Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} -
- +
diff --git a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx index a93382399d..a56a069f6a 100644 --- a/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx +++ b/src/routes/safe/components/Settings/ManageOwners/ReplaceOwnerModal/screens/Review/index.tsx @@ -7,7 +7,7 @@ import { useSelector } from 'react-redux' import { List } from 'immutable' import { ExplorerButton } from '@gnosis.pm/safe-react-components' -import { getExplorerInfo, getNetworkInfo } from 'src/config' +import { getExplorerInfo } from 'src/config' import CopyBtn from 'src/components/CopyBtn' import Identicon from 'src/components/Identicon' import Block from 'src/components/layout/Block' @@ -28,12 +28,10 @@ import { addressBookSelector } from 'src/logic/addressBook/store/selectors' import { styles } from './style' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' export const REPLACE_OWNER_SUBMIT_BTN_TEST_ID = 'replace-owner-submit-btn' -const { nativeCoin } = getNetworkInfo() - const useStyles = makeStyles(styles) type ReplaceOwnerProps = { @@ -65,7 +63,13 @@ export const ReviewReplaceOwnerModal = ({ const addressBook = useSelector(addressBookSelector) const ownersWithAddressBookName = owners ? getOwnersWithNameFromAddressBook(addressBook, owners) : List([]) - const { gasCostFormatted, txEstimationExecutionStatus, isExecution } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isExecution, + isCreation, + isOffChainSignature, + } = useEstimateTransactionGas({ txData: data, txRecipient: safeAddress, }) @@ -218,12 +222,13 @@ export const ReviewReplaceOwnerModal = ({ - - You're about to create a transaction and will have to confirm it with your currently connected wallet. -
- {`Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} -
- +
diff --git a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx index 3b838f705f..d28cbb5dbe 100644 --- a/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx +++ b/src/routes/safe/components/Settings/ThresholdSettings/ChangeThreshold/index.tsx @@ -3,7 +3,6 @@ import MenuItem from '@material-ui/core/MenuItem' import { makeStyles } from '@material-ui/core/styles' import Close from '@material-ui/icons/Close' import React, { useEffect, useState } from 'react' -import { getNetworkInfo } from 'src/config' import { styles } from './style' import Field from 'src/components/forms/Field' @@ -21,12 +20,10 @@ import { SafeOwner } from 'src/logic/safe/store/models/safe' import { List } from 'immutable' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' +import { TransactionFees } from 'src/components/TransactionsFees' const THRESHOLD_FIELD_NAME = 'threshold' -const { nativeCoin } = getNetworkInfo() - const useStyles = makeStyles(styles) type ChangeThresholdModalProps = { @@ -47,7 +44,13 @@ export const ChangeThresholdModal = ({ const classes = useStyles() const [data, setData] = useState('') - const { gasCostFormatted, txEstimationExecutionStatus } = useEstimateTransactionGas({ + const { + gasCostFormatted, + txEstimationExecutionStatus, + isCreation, + isExecution, + isOffChainSignature, + } = useEstimateTransactionGas({ txData: data, txRecipient: safeAddress, }) @@ -124,10 +127,13 @@ export const ChangeThresholdModal = ({ - - {`You're about to create a transaction and will have to confirm it with your currently connected wallet. Make sure you have ${gasCostFormatted} (fee price) ${nativeCoin.name} in this wallet to fund this confirmation.`} - - + diff --git a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx index 670c689594..cb2ac9f27e 100644 --- a/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx +++ b/src/routes/safe/components/Transactions/TxsTable/ExpandedTx/ApproveTxModal/index.tsx @@ -22,8 +22,7 @@ import { processTransaction } from 'src/logic/safe/store/actions/processTransact import { safeParamAddressFromStateSelector, safeThresholdSelector } from 'src/logic/safe/store/selectors' import { Transaction } from 'src/logic/safe/store/models/types/transaction' import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas' -import { TransactionFailText } from 'src/components/TransactionFailText' -import { TransactionFeesText } from 'src/components/TransactionsFeesText' +import { TransactionFees } from 'src/components/TransactionsFees' const useStyles = makeStyles(styles) @@ -145,14 +144,13 @@ export const ApproveTxModal = ({ )}
- -