diff --git a/src/components/TransactionsFees/index.tsx b/src/components/TransactionsFees/index.tsx
new file mode 100644
index 0000000000..c5e54f1970
--- /dev/null
+++ b/src/components/TransactionsFees/index.tsx
@@ -0,0 +1,43 @@
+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
+ gasCostFormatted: string
+ isExecution: boolean
+ isCreation: boolean
+ isOffChainSignature: boolean
+}
+const { nativeCoin } = getNetworkInfo()
+
+export const TransactionFees = ({
+ gasCostFormatted,
+ isExecution,
+ isCreation,
+ isOffChainSignature,
+ txEstimationExecutionStatus,
+}: 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/logic/hooks/useEstimateTransactionGas.tsx b/src/logic/hooks/useEstimateTransactionGas.tsx
index 2b3ee7ea4d..0acb86d8f7 100644
--- a/src/logic/hooks/useEstimateTransactionGas.tsx
+++ b/src/logic/hooks/useEstimateTransactionGas.tsx
@@ -1,11 +1,26 @@
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 } 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'
+import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
export enum EstimationStatus {
LOADING = 'LOADING',
@@ -16,13 +31,90 @@ export enum EstimationStatus {
const checkIfTxIsExecution = (threshold: number, preApprovingOwner?: string, txConfirmations?: number): boolean =>
txConfirmations === threshold || !!preApprovingOwner || threshold === 1
+const checkIfTxIsApproveAndExecution = (threshold: number, txConfirmations: number): boolean =>
+ txConfirmations + 1 === threshold
+
+const checkIfTxIsCreation = (txConfirmations: number): boolean => txConfirmations === 0
+
+type TransactionEstimationProps = {
+ txData: string
+ safeAddress: string
+ txRecipient: string
+ txConfirmations?: List
+ txAmount?: string
+ operation?: number
+ gasPrice?: string
+ gasToken?: string
+ refundReceiver?: string // Address of receiver of gas payment (or 0 if tx.origin).
+ safeTxGas?: number
+ from?: string
+ isExecution: boolean
+ isCreation: boolean
+ isOffChainSignature?: boolean
+ approvalAndExecution?: boolean
+}
+
+const estimateTransactionGas = async ({
+ txData,
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount,
+ operation,
+ gasPrice,
+ gasToken,
+ refundReceiver,
+ safeTxGas,
+ from,
+ isExecution,
+ isCreation,
+ isOffChainSignature = false,
+ approvalAndExecution,
+}: TransactionEstimationProps): Promise => {
+ if (isCreation) {
+ return estimateGasForTransactionCreation(safeAddress, txData, txRecipient, txAmount || '0', operation || CALL)
+ }
+
+ if (!from) {
+ throw new Error('No from provided for approving or execute transaction')
+ }
+
+ if (isExecution) {
+ return estimateGasForTransactionExecution({
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount: txAmount || '0',
+ txData,
+ operation: operation || CALL,
+ from,
+ gasPrice: gasPrice || '0',
+ gasToken: gasToken || ZERO_ADDRESS,
+ refundReceiver: refundReceiver || ZERO_ADDRESS,
+ safeTxGas: safeTxGas || 0,
+ approvalAndExecution,
+ })
+ }
+
+ return estimateGasForTransactionApproval({
+ safeAddress,
+ operation: operation || CALL,
+ txData,
+ txAmount: txAmount || '0',
+ txRecipient,
+ from,
+ isOffChainSignature,
+ })
+}
+
type UseEstimateTransactionGasProps = {
txData: string
txRecipient: string
- txConfirmations?: number
+ txConfirmations?: List
txAmount?: string
preApprovingOwner?: string
operation?: number
+ safeTxGas?: number
}
type TransactionGasEstimationResult = {
@@ -32,6 +124,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 = ({
@@ -41,6 +135,7 @@ export const useEstimateTransactionGas = ({
txAmount,
preApprovingOwner,
operation,
+ safeTxGas,
}: UseEstimateTransactionGasProps): TransactionGasEstimationResult => {
const [gasEstimation, setGasEstimation] = useState({
txEstimationExecutionStatus: EstimationStatus.LOADING,
@@ -49,10 +144,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(() => {
const estimateGas = async () => {
@@ -60,31 +159,50 @@ export const useEstimateTransactionGas = ({
return
}
+ const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations?.size)
+ const isCreation = checkIfTxIsCreation(txConfirmations?.size || 0)
+ const approvalAndExecution = checkIfTxIsApproveAndExecution(Number(threshold), txConfirmations?.size || 0)
+
try {
- const isExecution = checkIfTxIsExecution(Number(threshold), preApprovingOwner, txConfirmations)
+ const isOffChainSignature = checkIfOffChainSignatureIsPossible(isExecution, smartContractWallet, safeVersion)
const gasEstimation = await estimateTransactionGas({
safeAddress,
txRecipient,
txData,
txAmount,
+ txConfirmations,
isExecution,
+ isCreation,
+ isOffChainSignature,
operation,
+ from,
+ safeTxGas,
+ approvalAndExecution,
})
const gasPrice = await calculateGasPrice()
const estimatedGasCosts = gasEstimation * parseInt(gasPrice, 10)
const gasCost = fromTokenUnit(estimatedGasCosts, nativeCoin.decimals)
const gasCostFormatted = formatAmount(gasCost)
+ 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) {
+ console.warn(error.message)
// We put a fixed the amount of gas to let the user try to execute the tx, but it's not accurate so it will probably fail
const gasEstimation = 10000
const gasCost = fromTokenUnit(gasEstimation, nativeCoin.decimals)
@@ -95,7 +213,9 @@ export const useEstimateTransactionGas = ({
gasCost,
gasCostFormatted,
gasPrice: '1',
- isExecution: false,
+ isExecution,
+ isCreation,
+ isOffChainSignature: false,
})
}
}
@@ -110,7 +230,11 @@ export const useEstimateTransactionGas = ({
preApprovingOwner,
nativeCoin.decimals,
threshold,
+ from,
operation,
+ safeVersion,
+ smartContractWallet,
+ safeTxGas,
])
return gasEstimation
diff --git a/src/logic/safe/safeTxSigner.ts b/src/logic/safe/safeTxSigner.ts
index 52de1a3b68..4fce148dd0 100644
--- a/src/logic/safe/safeTxSigner.ts
+++ b/src/logic/safe/safeTxSigner.ts
@@ -1,31 +1,61 @@
-// 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'
+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 => {
+ 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 = 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/__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 5052b5cca3..0e318e3391 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,11 +9,10 @@ import {
CALL,
getApprovalTransaction,
getExecutionTransaction,
- SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES,
saveTxToHistory,
tryOffchainSigning,
} from 'src/logic/safe/transactions'
-import { estimateExecTransactionGas, 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 +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 { checkIfOffChainSignatureIsPossible, getPreValidatedSignatures } from 'src/logic/safe/safeTxSigner'
export interface CreateTransactionArgs {
navigateToTransactionsTab?: boolean
@@ -87,13 +86,18 @@ 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)
- const safeTxGas = safeTxGasArg || (await estimateExecTransactionGas(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)
-
const notificationsQueue = getNotificationsFromTxType(notifiedTransaction, origin)
const beforeExecutionKey = dispatch(enqueueSnackbar(notificationsQueue.beforeExecution))
@@ -118,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) {
@@ -136,9 +136,7 @@ const createTransaction = (
}
}
- const tx = isExecution
- ? await getExecutionTransaction(txArgs)
- : await getApprovalTransaction(safeInstance, safeTxHash)
+ const tx = isExecution ? getExecutionTransaction(txArgs) : getApprovalTransaction(safeInstance, safeTxHash)
const sendParams: PayableTx = { from, value: 0 }
// if not set owner management tests will fail on ganache
diff --git a/src/logic/safe/store/actions/processTransaction.ts b/src/logic/safe/store/actions/processTransaction.ts
index eced5a75c8..8d31d343ab 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 } 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,17 +52,15 @@ 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)
- let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, approveAndExecute && userAddress)
- // https://docs.gnosis.io/safe/docs/docs5/#pre-validated-signatures
+ const preApprovingOwner = approveAndExecute ? userAddress : undefined
+ let sigs = generateSignaturesFromTxConfirmations(tx.confirmations, preApprovingOwner)
+
if (!sigs) {
- sigs = `0x000000000000000000000000${from.replace(
- '0x',
- '',
- )}000000000000000000000000000000000000000000000000000000000000000001`
+ sigs = getPreValidatedSignatures(from)
}
const notificationsQueue = getNotificationsFromTxType(notifiedTransaction, tx.origin)
@@ -86,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) {
@@ -109,9 +103,7 @@ const processTransaction = ({
}
}
- transaction = isExecution
- ? await getExecutionTransaction(txArgs)
- : await getApprovalTransaction(safeInstance, tx.safeTxHash)
+ transaction = isExecution ? getExecutionTransaction(txArgs) : getApprovalTransaction(safeInstance, tx.safeTxHash)
const sendParams: any = { from, value: 0 }
@@ -196,5 +188,3 @@ const processTransaction = ({
return txHash
}
-
-export default processTransaction
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/logic/safe/transactions/gas.ts b/src/logic/safe/transactions/gas.ts
index b9ae685837..fa220bacd8 100644
--- a/src/logic/safe/transactions/gas.ts
+++ b/src/logic/safe/transactions/gas.ts
@@ -1,10 +1,12 @@
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 { calculateGasOf, EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
+import { getWeb3 } from 'src/logic/wallets/getWeb3'
import { sameString } from 'src/utils/strings'
+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,66 +25,6 @@ const parseRequiredTxGasResponse = (data: string): number => {
return data.match(/.{2}/g)?.reduce(reducer, 0)
}
-// https://docs.gnosis.io/safe/docs/contracts_signatures/#pre-validated-signatures
-export const getPreValidatedSignatures = (from: string): string => {
- return `0x000000000000000000000000${from.replace(
- EMPTY_DATA,
- '',
- )}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 ready to be executed)
- return 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 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 +127,7 @@ const calculateMinimumGasForTransaction = async (
return 0
}
-export const estimateExecTransactionGas = async (
+export const estimateGasForTransactionCreation = async (
safeAddress: string,
data: string,
to: string,
@@ -217,6 +159,97 @@ export const estimateExecTransactionGas = async (
)
} catch (error) {
console.info('Error calculating tx gas estimation', error.message)
+ throw error
+ }
+}
+
+type TransactionExecutionEstimationProps = {
+ txData: string
+ safeAddress: string
+ txRecipient: string
+ txConfirmations?: List
+ txAmount: string
+ operation: number
+ gasPrice: string
+ gasToken: string
+ refundReceiver: string // Address of receiver of gas payment (or 0 if tx.origin).
+ safeTxGas: number
+ from: string
+ approvalAndExecution?: boolean
+}
+
+export const estimateGasForTransactionExecution = async ({
+ safeAddress,
+ txRecipient,
+ txConfirmations,
+ txAmount,
+ txData,
+ operation,
+ gasPrice,
+ gasToken,
+ refundReceiver,
+ safeTxGas,
+ approvalAndExecution,
+}: TransactionExecutionEstimationProps): Promise => {
+ const safeInstance = await getGnosisSafeInstanceAt(safeAddress)
+ try {
+ if (approvalAndExecution) {
+ console.info(`Estimating transaction success for execution & approval...`)
+ // @todo (agustin) once we solve the problem with the preApprovingOwner, we need to use the method bellow (execTransaction) with sigs = generateSignaturesFromTxConfirmations(txConfirmations,from)
+ const gasEstimation = await estimateGasForTransactionCreation(
+ safeAddress,
+ txData,
+ txRecipient,
+ txAmount,
+ operation,
+ )
+ console.info(`Gas estimation successfully finished with gas amount: ${gasEstimation}`)
+ return gasEstimation
+ }
+ const sigs = generateSignaturesFromTxConfirmations(txConfirmations)
+ console.info(`Estimating transaction success for with gas amount: ${safeTxGas}...`)
+ await safeInstance.methods
+ .execTransaction(txRecipient, txAmount, txData, operation, safeTxGas, 0, gasPrice, gasToken, refundReceiver, sigs)
+ .call()
+
+ console.info(`Gas estimation successfully finished with gas amount: ${safeTxGas}`)
+ return safeTxGas
+ } catch (error) {
+ throw new Error(`Gas estimation failed with gas amount: ${safeTxGas}`)
+ }
+}
+
+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, txData, operation, 0, 0, 0, ZERO_ADDRESS, ZERO_ADDRESS, nonce)
+ .call({
+ from,
+ })
+ const approveTransactionTxData = await safeInstance.methods.approveHash(txHash).encodeABI()
+ return calculateGasOf(approveTransactionTxData, from, safeAddress)
}
diff --git a/src/logic/safe/transactions/send.ts b/src/logic/safe/transactions/send.ts
index 67bbab2f1a..289c8fe355 100644
--- a/src/logic/safe/transactions/send.ts
+++ b/src/logic/safe/transactions/send.ts
@@ -30,10 +30,7 @@ export const getTransactionHash = async ({
return txHash
}
-export const getApprovalTransaction = async (
- safeInstance: GnosisSafe,
- txHash: string,
-): Promise> => {
+export const getApprovalTransaction = (safeInstance: GnosisSafe, txHash: string): NonPayableTransactionObject => {
try {
return safeInstance.methods.approveHash(txHash)
} catch (err) {
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..532bf33b38 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,
@@ -156,10 +162,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 edc6376799..88c5c99437 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'
@@ -18,12 +17,12 @@ 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'
import { useEstimateTransactionGas } from 'src/logic/hooks/useEstimateTransactionGas'
-import { TransactionFailText } from 'src/components/TransactionFailText'
+import { TransactionFees } from 'src/components/TransactionsFees'
const useStyles = makeStyles(styles)
@@ -61,9 +60,8 @@ type Props = {
thresholdReached: boolean
tx: Transaction
}
-const { nativeCoin } = getNetworkInfo()
-const ApproveTxModal = ({
+export const ApproveTxModal = ({
canExecute,
isCancelTx = false,
isOpen,
@@ -81,12 +79,19 @@ 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,
+ safeTxGas: tx.safeTxGas,
operation: tx.operation,
})
@@ -141,16 +146,13 @@ 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.`}
-
-
-
+
-
- {`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/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'
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)
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 () => {