refactor: allow adding mocked transactions

This commit is contained in:
fernandomg 2020-05-22 16:50:17 -03:00
parent 376af39f3c
commit 6c1bc100b6
2 changed files with 132 additions and 40 deletions

View File

@ -1,4 +1,5 @@
import { push } from 'connected-react-router'
import { List, Map, fromJS } from 'immutable'
import semverSatisfies from 'semver/functions/satisfies'
import { onboardUser } from 'src/components/ConnectButton'
@ -12,10 +13,41 @@ import { ZERO_ADDRESS } from 'src/logic/wallets/ethAddresses'
import { EMPTY_DATA } from 'src/logic/wallets/ethTransactions'
import { providerSelector } from 'src/logic/wallets/store/selectors'
import { SAFELIST_ADDRESS } from 'src/routes/routes'
import fetchTransactions from 'src/routes/safe/store/actions/fetchTransactions'
import { addOrUpdateCancellationTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateCancellationTransactions'
import { addOrUpdateTransactions } from 'src/routes/safe/store/actions/transactions/addOrUpdateTransactions'
import { removeCancellationTransaction } from 'src/routes/safe/store/actions/transactions/removeCancellationTransaction'
import { removeTransaction } from 'src/routes/safe/store/actions/transactions/removeTransaction'
import { mockTransaction } from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation'
import fetchTransactions from './transactions/fetchTransactions'
export const removeTxFromStore = (tx, safeAddress, dispatch) => {
if (tx.isCancellationTx) {
dispatch(removeCancellationTransaction({ safeAddress, transaction: tx }))
} else {
dispatch(removeTransaction({ safeAddress, transaction: tx }))
}
}
export const storeTx = async (tx, safeAddress, dispatch) => {
if (tx.isCancellationTx) {
dispatch(
addOrUpdateCancellationTransactions({
safeAddress,
transactions: Map({ [`${tx.nonce}`]: tx }),
}),
)
} else {
dispatch(
addOrUpdateTransactions({
safeAddress,
transactions: List([tx]),
}),
)
}
}
const createTransaction = ({
safeAddress,
@ -42,7 +74,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, lastTx, safeInstance)
const nonce = Number(await getNewTxNonce(txNonce, lastTx, safeInstance))
const isExecution = await shouldExecuteTransaction(safeInstance, nonce, lastTx)
const safeVersion = await getCurrentSafeVersion(safeInstance)
const safeTxGas = await estimateSafeTxGas(safeInstance, safeAddress, txData, to, valueInWei, operation)
@ -78,9 +110,6 @@ const createTransaction = ({
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) {
@ -89,11 +118,7 @@ const createTransaction = ({
if (signature) {
closeSnackbar(beforeExecutionKey)
await saveTxToHistory({
...txArgs,
signature,
origin,
} as any)
await saveTxToHistory({ ...txArgs, signature, origin })
showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
dispatch(fetchTransactions(safeAddress))
@ -110,30 +135,39 @@ const createTransaction = ({
sendParams.gas = '7000000'
}
const txToMock = {
...txArgs,
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei,
}
const mockedTx = await mockTransaction(txToMock, safeAddress, state)
await tx
.send(sendParams)
.once('transactionHash', async (hash) => {
txHash = hash
closeSnackbar(beforeExecutionKey)
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
try {
await saveTxToHistory({
...txArgs,
txHash,
origin,
} as any)
txHash = hash
closeSnackbar(beforeExecutionKey)
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
await saveTxToHistory({ ...txArgs, txHash, origin })
await storeTx(mockedTx, safeAddress, dispatch)
dispatch(fetchTransactions(safeAddress))
} catch (err) {
console.error(err)
} catch (e) {
removeTxFromStore(mockedTx, safeAddress, dispatch)
}
})
.on('error', (error) => {
closeSnackbar(pendingExecutionKey)
removeTxFromStore(mockedTx, safeAddress, dispatch)
console.error('Tx error: ', error)
})
.then((receipt) => {
closeSnackbar(pendingExecutionKey)
.then(async (receipt) => {
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey)
}
showSnackbar(
isExecution
? notificationsQueue.afterExecution.noMoreConfirmationsNeeded
@ -142,6 +176,24 @@ const createTransaction = ({
closeSnackbar,
)
const toStoreTx = isExecution
? mockedTx.withMutations((record) => {
record
.set('executionTxHash', receipt.events.ExecutionSuccess.transactionHash)
.set('safeTxHash', receipt.events.ExecutionSuccess.returnValues.txHash)
.set('executor', from)
.set('isExecuted', true)
.set('isSuccessful', true)
.set('status', 'success')
})
: mockedTx.set('status', 'awaiting_confirmations')
await storeTx(
toStoreTx.set('confirmations', fromJS([makeConfirmation({ owner: from })])),
safeAddress,
dispatch,
)
dispatch(fetchTransactions(safeAddress))
return receipt.transactionHash
@ -149,7 +201,11 @@ const createTransaction = ({
} catch (err) {
console.error(err)
closeSnackbar(beforeExecutionKey)
closeSnackbar(pendingExecutionKey)
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey)
}
showSnackbar(notificationsQueue.afterExecutionError, enqueueSnackbar, closeSnackbar)
const executeDataUsedSignatures = safeInstance.contract.methods

View File

@ -1,3 +1,4 @@
import { fromJS } from 'immutable'
import semverSatisfies from 'semver/functions/satisfies'
import { getGnosisSafeInstanceAt } from 'src/logic/contracts/safeContracts'
@ -8,10 +9,16 @@ import { SAFE_VERSION_FOR_OFFCHAIN_SIGNATURES, tryOffchainSigning } from 'src/lo
import { getCurrentSafeVersion } from 'src/logic/safe/utils/safeVersion'
import { providerSelector } from 'src/logic/wallets/store/selectors'
import fetchSafe from 'src/routes/safe/store/actions/fetchSafe'
import fetchTransactions from 'src/routes/safe/store/actions/fetchTransactions'
import fetchTransactions from 'src/routes/safe/store/actions/transactions/fetchTransactions'
import {
isCancelTransaction,
mockTransaction,
} from 'src/routes/safe/store/actions/transactions/utils/transactionHelpers'
import { getLastTx, getNewTxNonce, shouldExecuteTransaction } from 'src/routes/safe/store/actions/utils'
import { getErrorMessage } from 'src/test/utils/ethereumErrors'
import { makeConfirmation } from '../models/confirmation'
import { storeTx } from './createTransaction'
const processTransaction = ({
approveAndExecute,
@ -47,6 +54,7 @@ const processTransaction = ({
let txHash
let transaction
const txArgs = {
...tx.toJS(), // merge the previous tx with new data
safeInstance,
to: tx.recipient,
valueInWei: tx.value,
@ -76,11 +84,7 @@ const processTransaction = ({
if (signature) {
closeSnackbar(beforeExecutionKey)
await saveTxToHistory({
...txArgs,
signature,
origin,
} as any)
await saveTxToHistory({ ...txArgs, signature })
showSnackbar(notificationsQueue.afterExecution.moreConfirmationsNeeded, enqueueSnackbar, closeSnackbar)
dispatch(fetchTransactions(safeAddress))
@ -97,6 +101,13 @@ const processTransaction = ({
sendParams.gas = '7000000'
}
const txToMock = {
...txArgs,
confirmations: [], // this is used to determine if a tx is pending or not. See `calculateTransactionStatus` helper
value: txArgs.valueInWei,
}
const mockedTx = await mockTransaction(txToMock, safeAddress, state)
await transaction
.send(sendParams)
.once('transactionHash', async (hash) => {
@ -106,20 +117,24 @@ const processTransaction = ({
pendingExecutionKey = showSnackbar(notificationsQueue.pendingExecution, enqueueSnackbar, closeSnackbar)
try {
await saveTxToHistory({
...txArgs,
txHash,
})
await saveTxToHistory({ ...txArgs, txHash })
await storeTx(mockedTx, safeAddress, dispatch)
dispatch(fetchTransactions(safeAddress))
} catch (err) {
console.error(err)
} catch (e) {
closeSnackbar(pendingExecutionKey)
await storeTx(tx, safeAddress, dispatch)
console.error(e)
}
})
.on('error', (error) => {
closeSnackbar(pendingExecutionKey)
storeTx(mockedTx.set('isSuccessful', false), safeAddress, dispatch)
console.error('Processing transaction error: ', error)
})
.then((receipt) => {
closeSnackbar(pendingExecutionKey)
.then(async (receipt) => {
if (pendingExecutionKey) {
closeSnackbar(pendingExecutionKey)
}
showSnackbar(
isExecution
@ -128,6 +143,27 @@ const processTransaction = ({
enqueueSnackbar,
closeSnackbar,
)
const toStoreTx = isExecution
? mockedTx.withMutations((record) => {
record
.set('executionTxHash', receipt.events.ExecutionSuccess.transactionHash)
.set('safeTxHash', receipt.events.ExecutionSuccess.returnValues.txHash)
.set('blockNumber', receipt.blockNumber)
.set('executionDate', record.submissionDate)
.set('executor', from)
.set('isExecuted', true)
.set('isSuccessful', true)
.set('status', isCancelTransaction(record, safeAddress) ? 'cancelled' : 'success')
})
: mockedTx.set('status', 'awaiting_confirmations')
await storeTx(
toStoreTx.set('confirmations', fromJS([...tx.confirmations, makeConfirmation({ owner: from })])),
safeAddress,
dispatch,
)
dispatch(fetchTransactions(safeAddress))
if (isExecution) {